Closed abhinav closed 5 months ago
CC @adonovan @timothy-king
Historically, the linter was disabled in gopls by default because memory consumption from its dependence on SSA analysis was too high. It was brought up in the golang-tools call today that this may not be less of an issue now.
Memory usage during SSA construction was indeed the reason nilness was never added to vet, but I'm not aware of any reason things might have changed since we made that decision. Would you like to try adding nilness to vet and measuring its performance (e.g. on go vet std
) before and after? If you're on Linux, you can use command time -f '%e %M' go vet std
to measure its elapsed time and peak memory usage.
Historically, the linter was disabled in gopls by default because memory consumption from its dependence on SSA analysis was too high. It was brought up in the golang-tools call today that this may not be less of an issue now.
Memory is certainly something to be concerned about, but it is not the blocker.
The concern is importing x/tools/go/ssa into cmd/vet and by extension the go release. ssa+x/tools/go/packages+go/ast+go/types put together create a compiler frontend for go. Right now the ssa package is maintained at a different cadence than the go compiler. Turning nilness on by default in cmd/vet requires confidence that x/tools/go/ssa is roughly as correct and as robust as the main go compiler and standard libraries go/types and go/ast. (Also with acceptable performance.) Any crashes in nilness coming from ssa would otherwise become a blocker for the rest of cmd/vet, which would be unacceptable. It would also need to handle any new go constructs and features at the same time as they are released for the language. The current balance is to have ssa (and its dependents like nilness) be [somewhat] easily available to other tools.
I think my main point is that you just need to be aware that this proposal does require a more significant ongoing staffing effort than just adding nilness to the list of checkers run in vet
.
Fair points, @timothy-king. I can see the risk of that.
@adonovan, I'm not on Linux, but looks like /usr/bin/time -l
provides similarly useful information.
Before:
% go version
go version go1.20.3 darwin/arm64
% /usr/bin/time -l go vet std
2.21 real 8.82 user 4.27 sys
118652928 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
590602 page reclaims
2799 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
10172 signals received
15612 voluntary context switches
150439 involuntary context switches
12489495277 instructions retired
8517906894 cycles elapsed
46400960 peak memory footprint
Adding nilness to the same version of Go:
% (cd src && ./make.bash)
# ...
% GOROOT=$(pwd) /usr/bin/time -l ./bin/go vet std
# ...
2.56 real 11.95 user 4.38 sys
158105600 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
655031 page reclaims
1811 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
12776 signals received
15422 voluntary context switches
159276 involuntary context switches
12162802284 instructions retired
8102358971 cycles elapsed
46417344 peak memory footprint
FYI, it reported some issues in std. The non-test ones appear to be intentional.
Thanks. Seems like a substantial increase in max RSS (+32%). I'm not sure that's a dealbreaker, but Tim's response is cogent, as is the fact that the standard library is not "clean", and that some of those violations are intentional. I mailed https://go.dev/cl/486675 to fix the good ones.
In the long term we are aiming for all of go vet to be enabled during go test, which means vet must be fast. My understanding is that SSA is below the performance bar. I wonder if the nilness check can be written using the simpler cfg analysis.
This summer we did some work to reduce the asymptotic cost of constructing the go/ssa representation, in particular eliminating the term that was proportional to the total number of dependencies. I just remeasured the cost of go vet -vettool=x std
with and without nilness in the multichecker, and to the nearest half second I consistently see 4.0s real 15s user with and 3.0s real 12s user without nilness, so still around 25%-33% cost, so no apparent benefit in this case. (The benefit higher up the application dependency graph is enormous though.)
We have plans for to improve things further, but the cost of SSA is still to high to justify adding nilness to vet for now.
Besides performance, the other aspect of this problem is that when new language features are added to the compiler, they need to be added to golang.org/x/go/ssa as well, and this can sometimes take a while; in the meantime, the vendored version of go/ssa is unable to process the dialect of Go understood by the compiler. While this not fundamentally different from any other analyzer, go/ssa is a more complex body of code, and as an example, the range-over-func feature of go1.23 only just landed in go/ssa this week (mostly my fault, but it doesn't change my point).
So, I think it would be impractical from a process perspective to add ssa-based analyzers to go vet, and I think we should decline this proposal.
This proposal has been declined as infeasible. — rsc for the proposal review group
I was also surprised that nilness
is not enabled by default in go vet or golangci-lint but it is in VS Code so I was surprised that it finds issues my linting CI setup does not. Now I wonder, are there any other tools available which are not enabled in go vet one could opt-in? Is that documented somewhere?
Now I wonder, are there any other tools available which are not enabled in go vet one could opt-in? Is that documented somewhere?
The gopls documentation explains the categories of analyzers it includes:
You can find gopls' internal analyzer configuration, which lists the particulars, here:
Summary
Consider enabling the nilness linter by default on
go vet
(and thereforego test
).Details
The nilness linter searches for potential nil pointer dereferences in code. It is not enabled by default for
go vet
orgopls
. Users can opt into it forgopls
with the analyses setting.It is not compiled into
go vet
, so users cannot opt-into it as part of a build process easily. Today, two options are:Install it manually with a fairly long import path, and use it with
go vet -vettool
Enable it in golangci-lint by adding it under the
linter-settings.govet.enable
configuration keyHistorically, the linter was disabled in gopls by default because memory consumption from its dependence on SSA analysis was too high. It was brought up in the golang-tools call today that this may not be less of an issue now.
This proposal suggests enabling the nilness linter by default in
go vet
andgopls
. If there are still good reasons not to do that, this proposal can be downgraded to building nilness intogo vet
and disabling it by default, allowing opt-in with a-nilness
flag.