New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/tools/go/analysis/passes/nilness: false positive with range over slice #65674
Comments
It's also wrong here:
My guess is that we need to handle This comparison is emitted for a range statement, so adding this check should fix both cases. |
Thanks, there's a missing nilness fact: when you enter a "range slice" loop, you learn that slice is not nil. It's a little tricky because the RangeStmt has been melted down, so you're left with a recursive equation about the loop index. The easiest fix is to peek at the "rangeindex" phi, but it's a little sleazy.
|
Change https://go.dev/cl/563476 mentions this issue: |
I think of these as quite similar to nilness's "impossible condition:" warnings. For this example #65674 (comment)
This is solvable by inferring from For this example #65674 (comment) :
The relevant comparison that is 'impossible' in the example is:
I do not have a non-'sleazy' suggestion for inferring |
(Hit the wrong buttons while editing. Reopening) |
I agree; it seems in keeping with the existing scope.
That's true, but nilness doesn't concern itself with lengths of things, and I don't think it should start down that road just to fix this issue.
The attached CL does warn that the slice is nil; see these test cases: if len(slice) > 3 {
_ = slice[2] // want "indexing a nil slice"
}
for i := 0; i < len(slice); i++ {
_ = slice[i] // want "indexing a nil slice"
} But it's not clear what "body" means in SSA; there's only a control flow graph.
Exactly; it's a slippery slope that leads to static bounds checking, and I want nilness to touch it. (BTW: Don't forget to convert URLs to https://go.dev/cl/563476 form for those not on our network.) |
Lasse suggested not visiting the range loop when the operand is a nil slice. The logic for that would be something like this: // Detect loop-over-nil-slice: a "{for,rangeindex}.loop"
// block whose condition is "x < len(y)" where y is nil.
// Don't visit the loop, to avoid printing "indexing a nil slice"
// for operation in the loop body.
if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok && b.Comment == "rangeindex.loop" || b.Comment == "for.loop" {
if binop, ok := If.Cond.(*ssa.BinOp); ok && binop.Op == token.LSS {
if call, ok := binop.Y.(*ssa.Call); ok {
common := call.Common()
if blt, ok := common.Value.(*ssa.Builtin); ok && blt.Name() == "len" {
if nilnessOf(stack, common.Args[0]) == isnil {
tsucc := b.Succs[0]
for _, d := range b.Dominees() {
if d != tsucc {
visit(d, stack)
}
}
return
}
}
}
}
} This logic is subtle--since it peeks at block comments--and incomplete; for example it can't handle a for-loop with a condition such as Its effect would be to suppress all the range- and for-loop errors: for range slice { // nothing to report here
}
for _, v := range slice { // want "indexing a nil slice"
_ = v
}
for i := range slice {
_ = slice[i] // want "indexing a nil slice"
}
for i := range slice {
if i < len(slice) {
_ = slice[i] // want "indexing a nil slice"
}
}
for i := 0; i < len(slice); i++ {
_ = slice[i] // want "indexing a nil slice"
} but to keep reporting an error in this case: if len(slice) > 3 {
_ = slice[2] // want "indexing a nil slice"
} Personally I don't like the complexity of the extra logic, and would rather be made aware of all of these errors since they indicate an authorial mistake (or that code to populate the slice has been temporarily commented out, but that's ok). Another option is simply to never report an error when indexing a nil slice. What do you think? |
In my opinion it is fine to report diagnostics here but be clear about what they mean. For example:
I agree that this is clearly an authorial mistake but I don't think the diagnostic message is very clear about what exactly the user has been doing wrong. There is not even an indexing operation in that line. The compiler under the hood might generate an indexing operation here but is this some that should be reported to the user (and something we can expect every user to know)? While I was able to figure out what was wrong fairly fast (partially because my example was very simple), it suprised me to see the nilness analyzer reporting a diagnostic. In the end it is your decision (and I can't judge the cost of the extra complexity) but I think there is room for improvement to make the finding less surprising. |
How about changing the message to "index/range of nil slice"? |
#65674 (comment)
|
Go version
tip
Output of
go env
in your module/workspace:What did you do?
The nilness Analyzer produces a false positive on:
What did you see happen?
The nilness analyzer reports
nil dereference in index operation
on the for loop even though the code is safe.What did you expect to see?
The analyzer should not report a diagnostic.
The text was updated successfully, but these errors were encountered: