Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static analysis of noexcept "violations" in C++

I'm trying to write exception safe code. I find that using C++11's noexcept specifier makes this goal a whole lot more achievable.

The general idea, of course, is that a function should be marked as 'noexcept' if, and only if all the functions that it calls are also marked as 'noexcept'.

The problem is that in a large code base, where patches from different people are often merged together, it is hard to ensure that this consistency is maintained.

So I would like to be able to run a static analysis that could list all places where a function that is marked 'nothrow' calls a function that is not marked as 'nothrow'.

As far as I can see in the man-page, GCC cannot help me here. Are there any stand-alone tools that can help me? Or maybe some other compilers?

like image 414
Kristian Spangsege Avatar asked Feb 01 '13 17:02

Kristian Spangsege


2 Answers

It certainly seems possible if you avoid pointer to functions (and deem them unsafe) by using the ABT of the program. Clang's AST is (despite its name) such an ABT: you will see both declarations and definitions of the functions. By doing your work one definition at a time, you will already have a good baseline.

On the other hand, I wonder whether this is practical. See, the problem is that any function performing a memory allocation is (voluntarily) marked as potentially throwing (because new never returns null, but throws bad_alloc instead). Therefore, your noexcept will be limited to a handful of functions in most cases.

And of course there are all the dynamic conditions like @GManNickG exposed, for example:

void foo(boost::optional<T> const t&) {
    if (not t) { return; }

    t->bar();
}

Even if T::bar is noexcept, dereferencing an optional<T> may throw (if there is nothing). Of course, this ignores the fact that we already ruled this out (here).

Without having clear conditions on when a function might throw, static analysis might prove... useless. The language idioms are designed with exceptions in mind.


Note: as a digression, the optional class could be rewritten so as not to exposed dereferencing, and thus be noexcept (if the callbacks are):

template <typename T>
class maybe {
public:

    template <typename OnNone, typename OnJust>
    void act(OnNone&& n, OnJust&& j) noexcept(noexcept(n()) and 
                                              noexcept(j(std::declval<T&>())))
    {
        if (not _assigned) { n(); return; }
        j(*reinterpret_cast<T*>(&_storage));
    }

private:
    std::aligned_storage<sizeof(T), alignof(T)>::type _storage;
    bool _assigned;
};

// No idea if this way of expressing the noexcept dependency is actually correct.
like image 139
Matthieu M. Avatar answered Nov 18 '22 17:11

Matthieu M.


The clang-tidy check bugprone-exception-escape attempts to find functions which may throw an exception directly or indirectly, when they should not. This includes checking functions marked with throw() or noexcept.

https://clang.llvm.org/extra/clang-tidy/checks/bugprone/exception-escape.html

like image 1
Bowie Owens Avatar answered Nov 18 '22 17:11

Bowie Owens