How to document all exceptions a function might throw?

rve picture rve · Dec 25, 2010 · Viewed 22.6k times · Source

If you have a public function which may throw an exception which uses other (private or public) helper functions which can also throw exceptions I think you should document what exceptions the public function can throw and this includes exceptions thrown by the helper functions.

Something like this (using Doxygen):

/** 
 * @throw Exception ...
 * @throw ExceptionThrownByHelper ...
 * @throw ExceptionThrownByHelpersHelper ...
 */
void theFunction() 
{ 
    helperWhichMayThrowException();
}

and helperWhichMayThrowException() also calls other functions which may throw exceptions.

To do this you can:

  1. recursively follow all functions theFunction() calls and look for exceptions thown by that function. This is a lot of work and you might forget to document an exception somewhere when you add an exception to a helper.
  2. catch all exceptions thrown by helpers in theFunction() and convert them so you are sure only the exceptions you specify are thrown. But then why use exceptions?
  3. do not worry about exceptions thrown by helper functions but then you can not unittest all exceptions because you do not know which exceptions can be thrown by the public function
  4. have some tool which (semi)automatically lists all exceptions thrown by helpers etc. I looked in the documentation of Doxygen but did not find a way to do this.

I would like to use option 4 but I have not found a good solution yet, maybe it is doable with Doxygen? Or maybe I just want to document to much???

edit: Maybe its not really clear but I am looking for an easy way to document all exceptions (preferably using Doxygen) a function might throw without manually checking all helper functions. An easy way includes 'do not document all exceptions' or 'catch and transform all exceptions in theFunction()'

Answer

Jason Williams picture Jason Williams · Dec 27, 2010

Fundamentally, what you ask is impossible in virtually every real-world situation.

There are two parts to documenting thrown exceptions.

1) The easy bit. Document the exceptions that are directly thrown in your method. You can do this by hand, but it's pretty laborious and if you fail to keep the docs in sync wiht the code the documentation becomes misleading (potentially worse than having no documentation at all, as you can only really trust documentation that you're sure is 100% accurate). My AtomineerUtils add-in makes this much easier to achieve, as it keeps the code and doc comments in sync with a minimum of effort.

2) The impossible bit. Document all exceptions that might "pass through" your method. This means recursing through the entire subtree of methods called by your method to see what they might throw. Why is it impossible? Well, in the simplest cases you will be statically binding to known methods, and can therefore scan them to see what they throw - moderately easy. But the majority of cases ultimately call dynamically bound methods (e.g. virtual methods, reflected or COM interfaces, external library methods in dlls, operating system APIs, etc) for which you cannot definitively work out what might be thrown (as you won't know what is called until you actually run the program on the end-user's PC - every PC is different, and the code executed on (e.g.) WinXP and Win7 could be quite different. Or imagine you call a virtual method and then somebody adds a plug-in to your program that overrides the method and throws a new type of exception). The only way to reliably handle this situation is to catch all exceptions in your method, and then re-throw specific ones that can then be documented precisely - if you can't do this, then documentation of exceptions is pretty much restricted to "commonly thrown and typically expected exceptions" in your method, leaving "exceptional errors" to be left largely undocumented and simply passed up to a higher level unhandled-exception catch blocks. (It is this horrible "undefined" behaviour of exceptions that often leads to the necessity of using catch(...) - academically it is "evil", but if you want your program to be bullet proof, you sometimes have to use catch-alls to be sure that unexpected situations don't assassinate your application).