Compare commits

..

10 Commits

Author SHA1 Message Date
dependabot[bot]
944e1d8441
build(deps): bump github.com/Abirdcfly/dupword from 0.1.1 to 0.1.3 (#5062)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 12s
Documentation / Build and deploy documentation (push) Failing after 40s
CI / go-mod (push) Failing after 1m12s
CI / golangci-lint (push) Failing after 1m36s
CI / tests-on-windows (push) Has been skipped
CI / tests-on-macos (push) Has been skipped
CI / tests-on-unix (1.22) (push) Has been skipped
CI / tests-on-unix (1.23) (push) Has been skipped
CI / commands (push) Has been skipped
CI / check_generated (push) Failing after 2m4s
CI / Installation script (local) (ubuntu-latest) (push) Failing after 2m32s
CI / Installation script (local) (macos-latest) (push) Has been cancelled
CI / Installation script (local) (windows-latest) (push) Has been cancelled
2024-10-08 16:36:15 +02:00
dependabot[bot]
b07c319f55
build(deps): bump github.com/go-critic/go-critic from 0.11.4 to 0.11.5 (#5061)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2024-10-08 14:33:24 +02:00
dependabot[bot]
31f2714643
build(deps): bump golangci/golangci-lint-action from 6.1.0 to 6.1.1 in the all group (#5059) 2024-10-07 22:53:34 +02:00
Anton Telyshev
61f8c0e892
build(deps): bump github.com/Antonboom/nilnil from 0.1.9 to 1.0.0 (#5058) 2024-10-06 16:08:12 +02:00
dependabot[bot]
9f4951f10d
build(deps): bump github.com/Antonboom/errname from 0.1.13 to 1.0.0 (#5057)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2024-10-06 15:29:59 +02:00
dependabot[bot]
10f281c1fd
build(deps): bump golang.org/x/tools from 0.25.0 to 0.26.0 (#5056) 2024-10-06 14:04:50 +02:00
Fernandez Ludovic
1626fe46f0 chore: update test workflow 2024-10-04 13:34:41 +02:00
Anton Telyshev
095e4952be
build(deps): bump github.com/Antonboom/testifylint from 1.4.3 to 1.5.0 (#5054) 2024-10-03 22:58:51 +02:00
Gavin Li
a86d8baa53
fix: type sizing when cross-compiling (32-bit) (#5053)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2024-10-03 22:33:16 +02:00
Ludovic Fernandez
094668e305
docs: improve 'install from sources' section (#5050) 2024-10-01 18:36:55 +02:00
16 changed files with 323 additions and 111 deletions

View File

@ -39,11 +39,9 @@ jobs:
# ex:
# - 1.18beta1 -> 1.18.0-beta.1
# - 1.18rc1 -> 1.18.0-rc.1
# TODO(ldez) must be changed after the first release of golangci-lint with go1.23
# go-version: ${{ env.GO_VERSION }}
go-version: '1.22'
go-version: ${{ env.GO_VERSION }}
- name: lint
uses: golangci/golangci-lint-action@v6.1.0
uses: golangci/golangci-lint-action@v6.1.1
with:
version: latest

View File

@ -890,6 +890,9 @@ linters-settings:
# Detects input and output parameters that have a type of pointer to referential type.
# https://go-critic.com/overview.html#ptrToRefParam
- ptrToRefParam
# Detects append all its data while range it.
# https://go-critic.com/overview.html#rangeAppendAll
- rangeAppendAll
# Detects expensive copies of `for` loop range expressions.
# https://go-critic.com/overview.html#rangeExprCopy
- rangeExprCopy
@ -1086,6 +1089,7 @@ linters-settings:
- preferStringWriter
- preferWriteByte
- ptrToRefParam
- rangeAppendAll
- rangeExprCopy
- rangeValCopy
- redundantSprint
@ -2115,14 +2119,17 @@ linters-settings:
min-complexity: 4
nilnil:
# In addition, detect opposite situation (simultaneous return of non-nil error and valid value).
# Default: false
detect-opposite: true
# List of return types to check.
# Default: ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"]
# Default: ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"]
checked-types:
- ptr
- chan
- func
- iface
- map
- chan
- ptr
- uintptr
- unsafeptr
@ -3269,7 +3276,9 @@ linters-settings:
- blank-import
- bool-compare
- compares
- contains
- empty
- encoded-compare
- error-is-as
- error-nil
- expected-actual
@ -3279,6 +3288,7 @@ linters-settings:
- len
- negative-positive
- nil-compare
- regexp
- require-error
- suite-broken-parallel
- suite-dont-use-pkg
@ -3292,15 +3302,17 @@ linters-settings:
disable-all: true
# Enable checkers by name
# (in addition to default
# blank-import, bool-compare, compares, empty, error-is-as, error-nil, expected-actual, go-require, float-compare,
# formatter, len, negative-positive, nil-compare, require-error, suite-broken-parallel, suite-dont-use-pkg,
# suite-extra-assert-call, suite-subtest-run, useless-assert
# blank-import, bool-compare, compares, contains, empty, encoded-compare, error-is-as, error-nil, expected-actual,
# go-require, float-compare, formatter, len, negative-positive, nil-compare, regexp, require-error,
# suite-broken-parallel, suite-dont-use-pkg, suite-extra-assert-call, suite-subtest-run, useless-assert
# ).
enable:
- blank-import
- bool-compare
- compares
- contains
- empty
- encoded-compare
- error-is-as
- error-nil
- expected-actual
@ -3310,6 +3322,7 @@ linters-settings:
- len
- negative-positive
- nil-compare
- regexp
- require-error
- suite-broken-parallel
- suite-dont-use-pkg

View File

@ -132,31 +132,26 @@ Colored output:
docker run -t --rm -v $(pwd):/app -w /app golangci/golangci-lint:{.LatestVersion} golangci-lint run -v
```
### Install from Source
### Install from Sources
Note: such `go install`/`go get` installation aren't guaranteed to work. We recommend using binary installation.
Such `go install`/`go get` or "tools pattern" installations aren't guaranteed to work.
<details>
<summary>Why?</summary>
We recommend using binary installation.
`go install`/`go get` installation isn't recommended because of the following points:
Those installations aren't recommended because of the following points:
1. Some users use `-u` flag for `go get`, which upgrades our dependencies. Resulting configuration wasn't tested and isn't guaranteed to work.
2. [`go.mod`](https://github.com/golangci/golangci-lint/blob/master/go.mod) replacement directive doesn't apply. It means a user will be using patched version of `golangci-lint` if we use such replacements.
3. We've encountered a lot of issues with Go modules hashes.
4. It allows installation from `master` branch which can't be considered stable.
5. It's slower than binary installation.
</details>
<div style="margin-top: 2em;">
1. Those installations are compiling golangci-lint locally, the Go version used to build will depend on your local Go version.
2. Some users use `-u` flag for `go get`, which upgrades our dependencies. Resulting binary was not tested and is not guaranteed to work.
3. When using "tools pattern", the dependencies of a tool can modify the dependencies of another. Resulting binary was not tested and is not guaranteed to work.
4. We've encountered issues with Go modules hashes due to unexpected recreation of dependency tags.
5. `go.mod` replacement directives don't apply transitively. It means a user will be using patched version of `golangci-lint` if we use such replacements.
6. It allows installation from main branch which can't be considered stable.
7. It's slower than binary installation.
```sh
go install github.com/golangci/golangci-lint/cmd/golangci-lint@{.LatestVersion}
```
</div>
## Next
[Quick Start: how to use `golangci-lint`](/welcome/quick-start/).

14
go.mod
View File

@ -6,10 +6,10 @@ require (
4d63.com/gocheckcompilerdirectives v1.2.1
4d63.com/gochecknoglobals v0.2.1
github.com/4meepo/tagalign v1.3.4
github.com/Abirdcfly/dupword v0.1.1
github.com/Antonboom/errname v0.1.13
github.com/Antonboom/nilnil v0.1.9
github.com/Antonboom/testifylint v1.4.3
github.com/Abirdcfly/dupword v0.1.3
github.com/Antonboom/errname v1.0.0
github.com/Antonboom/nilnil v1.0.0
github.com/Antonboom/testifylint v1.5.0
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c
github.com/Crocmagnon/fatcontext v0.5.2
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24
@ -38,7 +38,7 @@ require (
github.com/firefart/nonamedreturns v1.0.5
github.com/fzipp/gocyclo v0.6.0
github.com/ghostiam/protogetter v0.3.8
github.com/go-critic/go-critic v0.11.4
github.com/go-critic/go-critic v0.11.5
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/go-xmlfmt/xmlfmt v1.1.2
github.com/gofrs/flock v0.12.1
@ -126,7 +126,7 @@ require (
go-simpler.org/sloglint v0.7.2
go.uber.org/automaxprocs v1.6.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/tools v0.25.0
golang.org/x/tools v0.26.0
gopkg.in/yaml.v3 v3.0.1
honnef.co/go/tools v0.5.1
mvdan.cc/gofumpt v0.7.0
@ -192,7 +192,7 @@ require (
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

32
go.sum generated
View File

@ -37,14 +37,14 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8=
github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0=
github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY=
github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM=
github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM=
github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns=
github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ=
github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck=
github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA=
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI=
github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZk=
github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII=
github.com/Antonboom/testifylint v1.5.0 h1:dlUIsDMtCrZWUnvkaCz3quJCoIjaGi41GzjPBGkkJ8A=
github.com/Antonboom/testifylint v1.5.0/go.mod h1:wqaJbu0Blb5Wag2wv7Z5xt+CIV+eVLxtGZrlK13z3AE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@ -149,8 +149,8 @@ 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.8 h1:LYcXbYvybUyTIxN2Mj9h6rHrDZBDwZloPoKctWrFyJY=
github.com/ghostiam/protogetter v0.3.8/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU=
github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc=
github.com/go-critic/go-critic v0.11.5 h1:TkDTOn5v7EEngMxu8KbuFqFR43USaaH8XRJLz1jhVYA=
github.com/go-critic/go-critic v0.11.5/go.mod h1:wu6U7ny9PiaHaZHcvMDmdysMqvDem162Rh3zWTrqk8M=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -697,8 +697,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -777,8 +777,8 @@ 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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -860,8 +860,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -68,6 +68,7 @@
"preferStringWriter",
"preferWriteByte",
"ptrToRefParam",
"rangeAppendAll",
"rangeExprCopy",
"rangeValCopy",
"redundantSprint",
@ -2188,13 +2189,18 @@
"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": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"]
"enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"]
},
"default": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"]
"default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"]
}
}
},
@ -2905,7 +2911,9 @@
"blank-import",
"bool-compare",
"compares",
"contains",
"empty",
"encoded-compare",
"error-is-as",
"error-nil",
"expected-actual",
@ -2915,6 +2923,7 @@
"len",
"negative-positive",
"nil-compare",
"regexp",
"require-error",
"suite-broken-parallel",
"suite-dont-use-pkg",
@ -2928,16 +2937,19 @@
"blank-import",
"bool-compare",
"compares",
"contains",
"empty",
"encoded-compare",
"error-is-as",
"error-nil",
"expected-actual",
"float-compare",
"float-compare",
"formatter",
"go-require",
"len",
"negative-positive",
"nil-compare",
"regexp",
"require-error",
"suite-broken-parallel",
"suite-dont-use-pkg",
@ -2954,7 +2966,9 @@
"blank-import",
"bool-compare",
"compares",
"contains",
"empty",
"encoded-compare",
"error-is-as",
"error-nil",
"expected-actual",
@ -2964,6 +2978,7 @@
"len",
"negative-positive",
"nil-compare",
"regexp",
"require-error",
"suite-broken-parallel",
"suite-dont-use-pkg",

View File

@ -733,6 +733,7 @@ type NestifSettings struct {
}
type NilNilSettings struct {
DetectOpposite bool `mapstructure:"detect-opposite"`
CheckedTypes []string `mapstructure:"checked-types"`
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/scanner"
"go/types"
@ -164,6 +165,7 @@ func (lp *loadingPackage) loadFromSource(loadMode LoadMode) error {
pkg.Errors = append(pkg.Errors, lp.convertError(err)...)
},
GoVersion: rv, // TODO(ldez) temporary workaround
Sizes: types.SizesFor(build.Default.Compiler, build.Default.GOARCH),
}
_ = types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)

View File

@ -11,11 +11,11 @@ var (
ErrEndOfFile = errors.New("end of file")
errEndOfFile = errors.New("end of file")
EndOfFileError = errors.New("end of file") // want "the variable name `EndOfFileError` should conform to the `ErrXxx` format"
ErrorEndOfFile = errors.New("end of file") // want "the variable name `ErrorEndOfFile` should conform to the `ErrXxx` format"
EndOfFileErr = errors.New("end of file") // want "the variable name `EndOfFileErr` should conform to the `ErrXxx` format"
endOfFileError = errors.New("end of file") // want "the variable name `endOfFileError` should conform to the `errXxx` format"
errorEndOfFile = errors.New("end of file") // want "the variable name `errorEndOfFile` should conform to the `errXxx` format"
EndOfFileError = errors.New("end of file") // want "the sentinel error name `EndOfFileError` should conform to the `ErrXxx` format"
ErrorEndOfFile = errors.New("end of file") // want "the sentinel error name `ErrorEndOfFile` should conform to the `ErrXxx` format"
EndOfFileErr = errors.New("end of file") // want "the sentinel error name `EndOfFileErr` should conform to the `ErrXxx` format"
endOfFileError = errors.New("end of file") // want "the sentinel error name `endOfFileError` should conform to the `errXxx` format"
errorEndOfFile = errors.New("end of file") // want "the sentinel error name `errorEndOfFile` should conform to the `errXxx` format"
)
const maxSize = 256
@ -24,8 +24,8 @@ var (
ErrOutOfSize = fmt.Errorf("out of size (max %d)", maxSize)
errOutOfSize = fmt.Errorf("out of size (max %d)", maxSize)
OutOfSizeError = fmt.Errorf("out of size (max %d)", maxSize) // want "the variable name `OutOfSizeError` should conform to the `ErrXxx` format"
outOfSizeError = fmt.Errorf("out of size (max %d)", maxSize) // want "the variable name `outOfSizeError` should conform to the `errXxx` format"
OutOfSizeError = fmt.Errorf("out of size (max %d)", maxSize) // want "the sentinel error name `OutOfSizeError` should conform to the `ErrXxx` format"
outOfSizeError = fmt.Errorf("out of size (max %d)", maxSize) // want "the sentinel error name `outOfSizeError` should conform to the `errXxx` format"
)
func errInsideFuncIsNotSentinel() error {
@ -42,14 +42,14 @@ type DNSConfigError struct{}
func (D DNSConfigError) Error() string { return "DNS config error" }
type someTypeWithoutPtr struct{} // want "the type name `someTypeWithoutPtr` should conform to the `xxxError` format"
type someTypeWithoutPtr struct{} // want "the error type name `someTypeWithoutPtr` should conform to the `xxxError` format"
func (s someTypeWithoutPtr) Error() string { return "someTypeWithoutPtr" }
type SomeTypeWithoutPtr struct{} // want "the type name `SomeTypeWithoutPtr` should conform to the `XxxError` format"
type SomeTypeWithoutPtr struct{} // want "the error type name `SomeTypeWithoutPtr` should conform to the `XxxError` format"
func (s SomeTypeWithoutPtr) Error() string { return "SomeTypeWithoutPtr" }
type someTypeWithPtr struct{} // want "the type name `someTypeWithPtr` should conform to the `xxxError` format"
type someTypeWithPtr struct{} // want "the error type name `someTypeWithPtr` should conform to the `xxxError` format"
func (s *someTypeWithPtr) Error() string { return "someTypeWithPtr" }
type SomeTypeWithPtr struct{} // want "the type name `SomeTypeWithPtr` should conform to the `XxxError` format"
type SomeTypeWithPtr struct{} // want "the error type name `SomeTypeWithPtr` should conform to the `XxxError` format"
func (s *SomeTypeWithPtr) Error() string { return "SomeTypeWithPtr" }

View File

@ -1,8 +1,6 @@
package nilnil
import (
"strings"
"github.com/Antonboom/nilnil/pkg/analyzer"
"golang.org/x/tools/go/analysis"
@ -10,13 +8,16 @@ import (
"github.com/golangci/golangci-lint/pkg/goanalysis"
)
func New(cfg *config.NilNilSettings) *goanalysis.Linter {
func New(settings *config.NilNilSettings) *goanalysis.Linter {
a := analyzer.New()
cfgMap := make(map[string]map[string]any)
if cfg != nil && len(cfg.CheckedTypes) != 0 {
if settings != nil {
cfgMap[a.Name] = map[string]any{
"checked-types": strings.Join(cfg.CheckedTypes, ","),
"detect-opposite": settings.DetectOpposite,
}
if len(settings.CheckedTypes) != 0 {
cfgMap[a.Name]["checked-types"] = settings.CheckedTypes
}
}

View File

@ -3,8 +3,11 @@ package testdata
import (
"bytes"
"errors"
"fmt"
"go/token"
"io"
"net"
"net/http"
"os"
"unsafe"
@ -13,100 +16,100 @@ import (
type User struct{}
func primitivePtr() (*int, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func structPtr() (*User, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func emptyStructPtr() (*struct{}, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func anonymousStructPtr() (*struct{ ID string }, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func unsafePtr() (unsafe.Pointer, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func uintPtr() (uintptr, error) {
return 0, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return 0, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func uintPtr0b() (uintptr, error) {
return 0b0, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return 0b0, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func uintPtr0x() (uintptr, error) {
return 0x00, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return 0x00, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func uintPtr0o() (uintptr, error) {
return 0o000, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return 0o000, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func chBi() (chan int, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func chIn() (chan<- int, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func chOut() (<-chan int, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func fun() (func(), error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func funWithArgsAndResults() (func(a, b, c int) (int, int), error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func iface() (interface{}, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func anyType() (any, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func m1() (map[int]int, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func m2() (map[int]*User, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type mapAlias = map[int]*User
func m3() (mapAlias, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type Storage struct{}
func (s *Storage) GetUser() (*User, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func ifReturn() (*User, error) {
var s Storage
if _, err := s.GetUser(); err != nil {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
return new(User), nil
}
func forReturn() (*User, error) {
for {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
}
@ -114,15 +117,15 @@ func multipleReturn() (*User, error) {
var s Storage
if _, err := s.GetUser(); err != nil {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
if _, err := s.GetUser(); err != nil {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
if _, err := s.GetUser(); err != nil {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
return new(User), nil
@ -130,11 +133,11 @@ func multipleReturn() (*User, error) {
func nested() {
_ = func() (*User, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
_, _ = func() (*User, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}()
}
@ -145,7 +148,7 @@ func deeplyNested() {
_ = func() (*User, error) {
_ = func() {}
_ = func() int { return 0 }
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
}
return 0
@ -159,31 +162,31 @@ type MyError interface {
}
func myError() (*User, MyError) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
// Types.
func structPtrTypeExtPkg() (*os.File, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func primitivePtrTypeExtPkg() (*token.Token, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func funcTypeExtPkg() (http.HandlerFunc, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func ifaceTypeExtPkg() (io.Closer, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type closerAlias = io.Closer
func ifaceTypeAliasedExtPkg() (closerAlias, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type (
@ -195,29 +198,29 @@ type (
)
func structPtrType() (StructPtrType, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func primitivePtrType() (PrimitivePtrType, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func channelType() (ChannelType, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func funcType() (FuncType, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func ifaceType() (Checker, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type checkerAlias = Checker
func ifaceTypeAliased() (checkerAlias, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
type (
@ -226,7 +229,7 @@ type (
)
func ptrIntegerType() (PtrIntegerType, error) {
return nil, nil // want "return both the `nil` error and invalid value: use a sentinel error instead"
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
// Not checked at all.
@ -317,3 +320,57 @@ func implicitNil3() (*User, error) {
return nil, wrap(nil)
}
func wrap(err error) error { return err }
// Opposite.
func primitivePtrTypeOpposite() (*int, error) {
if false {
return nil, io.EOF
}
return new(int), errors.New("validation failed")
}
func structPtrTypeOpposite() (*User, error) {
if false {
return nil, io.EOF
}
return new(User), fmt.Errorf("invalid %v", 42)
}
func unsafePtrOpposite() (unsafe.Pointer, error) {
if false {
return nil, io.EOF
}
var i int
return unsafe.Pointer(&i), io.EOF
}
func uintPtrOpposite() (uintptr, error) {
if false {
return 0, io.EOF
}
return 0xc82000c290, wrap(io.EOF)
}
func channelTypeOpposite() (ChannelType, error) {
if false {
return nil, io.EOF
}
return make(ChannelType), fmt.Errorf("wrapped: %w", io.EOF)
}
func funcTypeOpposite() (FuncType, error) {
if false {
return nil, io.EOF
}
return func(i int) int {
return 0
}, errors.New("no func type, please")
}
func ifaceTypeOpposite() (io.Reader, error) {
if false {
return nil, io.EOF
}
return new(bytes.Buffer), new(net.AddrError)
}

View File

@ -0,0 +1,83 @@
//golangcitest:args -Enilnil
//golangcitest:config_path testdata/nilnil_detect_opposite.yml
package testdata
import (
"bytes"
"errors"
"fmt"
"io"
"net"
"unsafe"
)
func primitivePtrTypeOpposite() (*int, error) {
if false {
return nil, io.EOF
}
return new(int), errors.New("validation failed") // want "return both a non-nil error and a valid value: use separate returns instead"
}
func structPtrTypeOpposite() (*User, error) {
if false {
return nil, io.EOF
}
return new(User), fmt.Errorf("invalid %v", 42) // want "return both a non-nil error and a valid value: use separate returns instead"
}
func unsafePtrOpposite() (unsafe.Pointer, error) {
if false {
return nil, io.EOF
}
var i int
return unsafe.Pointer(&i), io.EOF // want "return both a non-nil error and a valid value: use separate returns instead"
}
func uintPtrOpposite() (uintptr, error) {
if false {
return 0, io.EOF
}
return 0xc82000c290, wrap(io.EOF) // want "return both a non-nil error and a valid value: use separate returns instead"
}
func channelTypeOpposite() (ChannelType, error) {
if false {
return nil, io.EOF
}
return make(ChannelType), fmt.Errorf("wrapped: %w", io.EOF) // want "return both a non-nil error and a valid value: use separate returns instead"
}
func funcTypeOpposite() (FuncType, error) {
if false {
return nil, io.EOF
}
return func(i int) int { // want "return both a non-nil error and a valid value: use separate returns instead"
return 0
}, errors.New("no func type, please")
}
func ifaceTypeOpposite() (io.Reader, error) {
if false {
return nil, io.EOF
}
return new(bytes.Buffer), new(net.AddrError) // want "return both a non-nil error and a valid value: use separate returns instead"
}
type (
User struct{}
StructPtrType *User
PrimitivePtrType *int
ChannelType chan int
FuncType func(int) int
Checker interface{ Check() }
)
func wrap(err error) error { return err }
func structPtr() (*int, error) {
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func structPtrValid() (*int, error) {
return new(int), nil
}

View File

@ -0,0 +1,3 @@
linters-settings:
nilnil:
detect-opposite: true

View File

@ -0,0 +1,39 @@
//golangcitest:args -Enilnil
//golangcitest:config_path testdata/nilnil_pointers_only.yml
package testdata
import "unsafe"
type User struct{}
func primitivePtr() (*int, error) {
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func structPtr() (*User, error) {
return nil, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func unsafePtr() (unsafe.Pointer, error) {
return nil, nil
}
func uintPtr0o() (uintptr, error) {
return 0o000, nil // want "return both a `nil` error and an invalid value: use a sentinel error instead"
}
func chBi() (chan int, error) {
return nil, nil
}
func fun() (func(), error) {
return nil, nil
}
func anyType() (any, error) {
return nil, nil
}
func m1() (map[int]int, error) {
return nil, nil
}

View File

@ -0,0 +1,5 @@
linters-settings:
nilnil:
checked-types:
- ptr
- uintptr

View File

@ -225,5 +225,5 @@ func TestPrinter_Print_multiple(t *testing.T) {
goldenJSON, err := os.ReadFile(filepath.Join("testdata", "golden-json.json"))
require.NoError(t, err)
assert.Equal(t, string(goldenJSON), stdOutBuffer.String())
assert.JSONEq(t, string(goldenJSON), stdOutBuffer.String())
}