On Thu, Jun 27, 2019 at 10:06 AM Axel Wagner
<
axel.wa...@googlemail.com> wrote:
>
> Can you post some working code to the playground? ISTM that the code you shown wouldn't compile (X has both a Next field and a Next method). Also, the receiver is called X, but you return an *Item. That's not totally hypothetical as a follow-up, because in general, linked lists are an example of where IMO a nil-value can be made a completely useful implementation of an interface, by putting a nil-check in the methods. However, it's hard to talk about it concretely, without working code to adapt to show the idea :)
Sorry for the sloppy code. The first email I sent has a working
example to illustrate. My real code where I had this was in the
context of an ssh-based library with a bastion host. Here's a summary
version:
https://play.golang.org/p/tNmWWb_28qZ
When I first wrote this, I had a Host struct, with func (h Host)
GetVia() *Host . After it worked, I changed the Host to an interface,
HostIntf and added Via() HostIntf function to it, and ended up with an
enabled recursion, because h.Via() is never nil.
>
> On Thu, Jun 27, 2019 at 5:46 PM Burak Serdar <
bse...@ieee.org> wrote:
>>
>> - function returns an interface Y
>> - there is a return X where X is not of type Y and is a reference
>> - X is not assigned a value in at least one code path
>> - there are no nil-checks for X
>
>
> IMO this is not sufficient to avoid false positives. For one, if nil is a valid implementation of an interface, returning a nil-value of that type is of course totally fine. Then, with the sort.StringSlice example, there are no nil-checks in any methods - but there don't *have* to be, because `len` on a nil-slice returns zero and the contract of the interface guarantees that Swap and Less are never called with out-of-bound indices. Note, that there
If you do "return sort.StringSlice(nil)", then the return value is
created/assigned in the function body, so it wouldn't alert. It would
only alert if the function gets a reference value from somewhere else,
converts it to an interface and returns it. So the following would be
ok:
func f() sort.Interface {
return sort.StringSlice(nil)
}
But this would be a false-positive:
func g() sort.StringSlice {
return sort.StringSlice(nil)
}
func f() sort.Interface {
return g()
}
is no way to check for this programmatically - for a tool, if
StringSlice.Swap is ever called, with any arguments, on a nil-value,
it would panic.
> So the implementation of sort.StringSlice is totally fine as is and it's totally reasonable to use a nil-value as a sort.Interface (e.g. say you declare a variable of that type and then fill it conditionally with append - you might end up with a nil-slice) but would be reported as a false-positive.
You are talking about analyzing what happens after function returns. I
am talking about only analyzing the function that causes the possible
nil-value-interface. Above, only the function f() would be analyzed,
and the first copy would be ok, and the second copy not, which, in a
way makes sense because f() doesn't know what g() does, and if g()
returns a nil instead of a nil-value-interface, then f() will hide
that nil.