Information about which exceptions can be thrown from a function
Hi! I have one interesting idea - sometimes we want to know, which exceptions can be thrown by a function (since in modern C++ we haven't exception specification). It will be useful for: documentation, writing proper exceptions handlers for functions, etc.
I understand that it can be done only for existent source code and cannot - for external binary deps.
What do you think about the idea?
Yeah, for external binary deps you'd have to resort to disassembly and trying to find the runtime's hooks for the throw instruction, alongside with the appropriate constructors for the exception types, C++ also twists this with no top class for what types can appear in a throw instruction.
Calculating the maximum set of possible exception types for a function call should be easy enough, as it is a few traversals only. The key things that bring in problems are:
- removing the "caught and not rethrown" exception types that appear in the subsequent function calls from your function
- dynamic binding / polymorphism,
extern templates, dynamically linked function calls (such as that coming in withdlopen(), CodeCompass can so far only perform static fact extraction, see #198 only works for, as you phrased, existing and known code) - recursion and the possible infinity (or at least, from the "static analysis'" and human standpoint, infinity) of the call stack.
For the 3rd one, imagine that you have a highly recursive indirect call chain such as A-A-B-C-D-E-A-A-B-C-D-E-A-F-C-D-E-B-.... in which each function could add, even if we don't consider a path-sensitive analysis, new types to the "set of possible thrown objects". While this set is mathematically not infinite (it has to be a subset of all known valid types anyways...), calculating this will bring forth the same problems people in the Clang Static Analyzer (the Shetland pony of our sister project, CodeChecker) suffer from, namely infeasibility of total analysis.
For an initial version, of course, dumping out what this function in particular can throw (with no respect to inner function calls...) sounds easy. For the rest, here comes the flood…
One big problem with the throw()-declarator was that it compiled into runtime checks, and there were little to no true and thorough compiler assistance to keep your code updated when new types / functions / whatever are added - maybe for the very same reasons mentioned above.
Thank you for your suggestion! :blush: We'll discuss it. We have a thing called "Software Technology Lab" for Masters Students at our local neighbouring university, maybe this thing's initial implementation will be a good learning practise for someone who lacks an assignment.
For the 3rd one, imagine that you have a highly recursive indirect call chain such as A-A-B-C-D-E-A-A-B-C-D-E-A-F-C-D-E-B-.... in which each function could add, even if we don't consider a path-sensitive analysis, new types to the "set of possible thrown objects". While this set is mathematically not infinite
You are right. But for this kind of problem there is widely-used solution (even in enterprise analyzers - "The function is too complex for complete analysis. The information can be incomplete". Clion do something similar for large files because of some internal limitations of static analyzer engine).
One big problem with the throw()-declarator was that it compiled into runtime checks, and there were little to no true and thorough compiler assistance to keep your code updated when new types / functions / whatever are added - maybe for the very same reasons mentioned above.
Yes. Any change of program can bring new exception. How to reduce size of required for analysis code - I don;t know yet exactly.
Maybe make sense to check other proprietary solutions - probably some of them has some kind of exception analysis. Here can be a good start point for research - seems like PC-lint has some kind of static exception analysis, but I did't test it (yet).