This is an archive of the discontinued LLVM Phabricator instance.

Add new warning to Clang to detect when all code paths in a function has a call back to the function.
ClosedPublic

Authored by rtrieu on Oct 8 2013, 8:13 PM.

Details

Reviewers
rsmith
Summary

Implement a warning to detect when a function will call itself recursively on every code path. If a program ever calls such a function, the function will attempt to call itself until it runs out of stack space.

This warning searches the CFG to determine if every codepath results in a self call. In addition to the test for this warning, several other tests needed to be fixed, and a pragma to prevent this warning where Clang really wants a stack overflow.

Testing with this warning has already caught several buggy functions. Common mistakes include: incorrect namespaces, wrapper classes not forwarding calls properly, similarly named member function and data member, and failing to call an overload of the same function. When run outside of template instantiations, all true positives. In template instantiations, only 25% true positive. Therefore, this warning is disabled in template instantiations. An example of such a false positive is in the test cases.

Diff Detail

Event Timeline

rtrieu updated this revision to Unknown Object (????).Oct 14 2013, 1:10 PM

Warn on non-templated member functions of templated classes.

rtrieu updated this revision to Unknown Object (????).Oct 24 2013, 8:52 PM

Fix misspelling of function.

silvas added a comment.Nov 8 2013, 3:20 PM

Have you measured the compile-time overhead of this check?

Also, the randomly strewn -Wno-infinite-recursion around the test suite is kind of worrying. Can those tests just be changed (in separate commits) to preserve what they are testing but not have spurious infinite recursion? I can't imagine that infinite recursion is somehow essential to what they are testing.

At a higher level, is this really needed as a compiler warning? I mean, it's nice and all to detect these things statically, but is this really something that needs to be happening on every build? Could we just do this in the static analyzer? In practice, I can't imagine any of these being hard to debug "while developing", since they will always result in a stack overflow the second they are called (and then you just look at the core file (or the debugger that your IDE attached) to see which function it was) (also, maybe Asan would intervene before the stack overflow and give you a nice pretty error report?). So essentially it seems like this is finding bugs in code that has no test coverage and has never been executed in practice; that kind of "cleaning out crusty unused parts of the codebase" seems like it would be better left to the static analyzer. Or do these kinds of infinite recursion things come up so often during development that having it be a warning is a big time-saver for developers?

rsmith added inline comments.Nov 12 2013, 10:10 PM
lib/Sema/AnalysisBasedWarnings.cpp
85–87

These names are a bit confusing. They seem to mean:

Unknown: no paths to here have been visited
AfterFunctionCall: no paths to here have been found that don't recurse
Reachable: paths to here have been found that don't recurse

How about FoundNoPath, FoundPath, FoundPathWithNoRecursiveCall or similar?

96–97

Once you set the exit block to Reachable, you can bail out of the traversal early.

107

Compare canonical declarations here.

108–109

If the function is non-virtual, you should warn even if the object argument is different.

rtrieu updated this revision to Unknown Object (????).Nov 20 2013, 5:41 PM

Switch -Winfinite-recursion to DefaultIgnore, in line with the other CFG warnings. This allows the other tests to be reverted. Also made the suggested changes from Richard Smith.

rsmith accepted this revision.Dec 17 2013, 6:25 PM

LGTM with minor tweaks.

I'm still not sure we've got the function template specialization case right, but I think this adds most of the value as-is, and I think you're right to blacklist an area with known false positives over not having the warning at all.

lib/Lex/Pragma.cpp
934–936

Somewhat unrelated to your patch, but this looks broken (at -O1 or above). How about replacing this with:

static void DebugOverflowStack() {
  void (*volatile Self)() = DebugOverflowStack;
  self();
}

That should both suppress your warning (and presumably the MSVC warning) and make this function work reliably.

lib/Sema/AnalysisBasedWarnings.cpp
90–93

For new code, please follow the coding style's formatting rules (checkForFunctionCall, States, State, ExitID, and " &" not "& ").

96–99

Maybe

if (States[ID] >= State)
  return;

States[ID] = State;
// ...
rtrieu closed this revision.Dec 20 2013, 6:41 PM

r197849 has the fix to allow the overflow to work in optimized code
r197853 is the commit for the warning