Description
With trait objects it is not so rare to have code like
impl Foo for Box<Foo> {
fn bar(&self, x: uint) {
// call the `bar` method in the vtable, could try:
// self.bar(x);
// (*self).bar(x);
// (**self).bar(x);
}
}
One might first write self.bar(x)
, but this will be calling that same bar, via since Box<Foo>
has a "real" bar
method itself. The correct procedure is (**self).bar(x)
. This would helped by detecting the most obvious cases of a function calling itself in a nonterminating way. The most obvious way that is likely to have low risk of false positives is detecting if the CFG of a function reaches a self-call unconditionally. e.g.
// these receive a warning:
fn foo() { foo() }
fn bar(x: uint) {
bar(x - 1);
}
fn baz(x: uint) -> uint {
if x == 0 {
println!("hi")
}
2 * baz(x + 1)
}
// this is fine:
fn qux(x: uint) {
if x == 0 { loop {} } // (could be `return 1`, etc. etc.)
qux(x - 1) + 1
}
Notably, it does not matter if the arguments to the self call are different, since the call will always occur, and it also does not matter if the self-call is in tail position.
There is the possibility that someone is using infinite recursion to achieve an infinite loop, it doesn't seem crazy to recommend that they use loop
instead.
A more conservative alternative would be just detecting the method call in the first example, that is, a function that consists of a single expression that is a self-call.