Search code examples
c++movecompiler-warningsstatic-analysis

Detect use after move during compilation?


The clang-tidy static analyzer detects uses of variables after being moved.

class a_class {
    std::unique_ptr<int> p_;
 public:
    auto p() -> auto& {return p_;}
    void f() const {}
};

int main() {
    auto aa = a_class{};
    [[maybe_unused]] auto bb = std::move(aa);
    aa.f();
}
 error: Method called on moved-from object 'aa' [clang-analyzer-cplusplus.Move,-warnings-as-errors]

This great! ©.

How can I make the compiler, clang or GCC detect the same issue too? Either by activating some warning option or by some (non-standard?) attribute?

I tried using -Wmove in clang and the [[consumed]] attribute but they didn't help. Perhaps I used them incorrectly.

The code is here: https://godbolt.org/z/18hr4vn7x (the lower panel is clang-tidy and the mid panel on the right is the [empty] compiler output)

Is there a chance a compiler will warn about this or it is just too costly for the compiler to check for this pattern?


Solution

  • I found one way to do it, using attributes in clang. . (A GCC or a more standard solution is still welcome.)

    1. needs clang 6 or higher
    2. mark the class as "consumable"
    3. mark the method(s) "callable-when-unconsumed" (not sure how to make this the default)
    class [[clang::consumable(unconsumed)]] a_class {
        std::unique_ptr<int> p_;
    
    public:
        [[clang::callable_when(unconsumed)]]
        void f() {}
    
        // private: [[clang::set_typestate(consumed)]] void invalidate() {}  // not needed but good to know
    };
    

    https://godbolt.org/z/45q8vzdnc

    The recipe is simplified from https://awesomekling.github.io/Catching-use-after-move-bugs-with-Clang-consumed-annotations/ . I couldn't find detailed documentation on how to use the these features.

    It is simplified because:

    a) it seems that "clang-consumable" moved object becomes "consumed" by default when moved-from, so it is not necessary to write a special function to invalidate if not necessary (no need for [[clang::set_typestate(consumed)]]).

    b) constructors seem to leave the object in an unconsumed state by default (no need for [[clang::return_typestate(unconsumed)]]);