ICA checks

Check name Description Example

char-in-ctype-pred

Detects when char or signed char is passed to the predicate functions from <cctype> (or <ctype.h>), which may be undefined befavior. From cppreference

"Like all other functions from <cctype>, the behavior of std::isalpha is undefined if the argument's value is neither representable as unsigned char nor equal to EOF. To use these functions safely with plain chars (or signed chars), the argument should first be converted to unsigned char"
char c = getchar();
bool b = std::isalpha(c); // warning
int count_alphas(const std::string& s)
{
    return std::count_if(s.begin(), s.end(),
                    // static_cast(std::isalpha)         // wrong
                    // [](int c){ return std::isalpha(c); }           // wrong
                    // [](char c){ return std::isalpha(c); }          // wrong
                         [](unsigned char c){ return std::isalpha(c); } // correct
                        );
}
            

emplace-default-value

Detects, when default-constructed value is passed to map `emplace` method and suggests to use `try_emplace` method instead
std::unordered_map mis;
mis.emplace(0, std::string{}); // warning
mis.try_emplace(0) // OK
mis.emplace(1, std::string{"one"}); // OK
            

erase-in-loop

Detects when `erase` is called on iterator which is incremented in a for loop. Majority of containers invalidates an iterator which is erased. Does not produce a warning if `return` or `break` statement is present at the same level as `erase`.
std::map m;
auto it = m.begin(), end = m.end();
for (; it != end; ++it) {
    if (it->first == 123) {
        m.erase(it); // warning
    }
}
auto it = m.begin(), end = m.end();
for (; it != end; ++it) {
    if (it->first == 123) {
        m.erase(it); // no warning
        break;
    }
}
            

find-emplace

Detects `find` + `insert` pattern for STL-like containers. This is inefficient because `find` tries to find an element, traversing the container and then `insert` traverses the container one more time. In most cases this could be replaced with one `insert` or `emplace`
// Inefficient:
auto it = map.find(key);
if (it == map.end()) {
   map.insert(key, value);
} else {
   it->second = value;
}
 // Efficient:
auto [it, inserted] = map.emplace(key, value);
if (!inserted) {
   it->second = value;
}
            

redundant-noexcept

Checks `noexcept` specified functions for calls of potentially throwing functions which bodies aren't seen in current translation unit, as it causes insertion of `std::terminate`
void call_new() noexcept // warning
{
    const char * str = new char[4]{"lol"}; // 'new' caused warning
}
int get_min(int l, int r) noexcept
{
    return std::min(l, r); // 'std::min' is not noexcept-specified
}
            

remove-c_str

Suggests removing unnecessary `c_str()` calls if overload with `std::string_view` exists.
void func(const char *);
void func(std::string_view);
std::string key = "key";
func(key.c_str()); // warning
func(key) // ok
            

bad-rand

Detects:
  • usage of `std::rand` (which is not thread-safe)
  • usage of `std::random_shuffle` (deprecated in C++14, removed in C++17)
  • using constants as seed
  • generation of multiple numbers by `std::random_device` (which may be a long operation as it's execution time isn't specified)
  • lack of `std::*_distribution` usage
  • one-shot usage of random engine in function
  • // good example
    struct random_struct
    {
        random_struct()
            : m_rand_engine(std::random_device{}()) // construct random device and call to get seed for random engine construction
        {}
        int get_rand(int from, int to)
        {
            std::uniform_int_distribution distribution(from, to); // distribution construction is quite cheap
            return distribution(m_rand_engine);
        }
    private:
        mutable std::mt19937 m_rand_engine;
    };
                

    for-range-const

    Detects if `const` qualifier can be added to initialization stmt in for-range loop. Doesn't warn if the variable is used for template deduction
    std::vector from;
    std::vector to;
    for (auto & el : from) { // warning
        to.push_back(el); // causes warning
    }
                

    const-param

    Detects:
  • unnecessary absence of `const` qualifier of function parameter
  • non-modified parameters which were got by rvalue-reference
  • void ok(const int & i) {}
    void param_could_be_const(int & i) {} // warning
    void rvalue_is_never_moved(int && i) {} // warning
                

    improper-move

    Detects:
  • move from non-`const` lvalue (may invalidate provided data)
  • if `std::move` argument is already rvalue
  • move to `const` lvalue reference
  • move of trivial types (it's identical to copy)
  • `const` rvalue parameters
  • template 
    auto get_rvalue(T & t) { return std::move(t); } // 'move' invalidates 't'
    void foo(const std::string s)
    {
        auto substr = s.substr(1); // move of rvalue
        foo(std::move(substr)); // move to const lvalue
        int a = 3, b;
        b = std::move(a) // move of trivial type
    }
    void boo(const std::string && s) {} // const rvalue parameter
                

    init-members

    Detects if member can be initialized in initialization list in constructor instead of assignment within body
    struct Struct
    {
        Struct (const int * arr, std::size_t size)
            : m_data(arr, arr + size) // good
        {
            m_size = size; // bad, warning
        }
    private:
        std::vector m_data;
        std::size_t m_size;
    };
                

    correct-iterator

    Detects if an iterator type doesn't declare all needed members
    // good iterator
    struct iterator
    {
        using value_type = long;
        using reference = value_type &;
        using pointer = value_type *;
        using difference_type = std::ptrdiff_t;
        using iterator_category = std::bidirectional_iterator_tag;
        iterator(const iterator &) = default;
        iterator & operator = (const iterator &) = default;
        reference operator * ();
        pointer operator -> ();
        iterator & operator ++ ();
        iterator operator ++ (int);
        iterator & operator -- ();
        iterator operator -- (int);
        bool operator == (const iterator &) const;
        bool operator != (const iterator &) const;
    };
                

    const-cast-member

    Detects if `const_cast` is applied to member. Usage of `mutable` is preferred
    struct Struct
    {
        int get_num() const
        {
            std::uniform_int_distribution dist(0, 10);
            return dist(const_cast(m_rand_engine)); // warning
        }
    private:
        std::mt19937 m_rand_engine; // assume it's properly initialized
    };
                

    temporary-in-ctor

    Detects if temporary used instead of direct writing into member
    struct Struct
    {
        Struct(const int * arr, std::size_t size)
        {
            std::vector to_fill; // warning
            if (arr) {
                for (std::size_t i = 0; i < size; ++i) {
                    to_fill.push_back(arr[i]);
                }
                data = std::move(to_fill);
            }
        }
    private:
        std::vector data;
    };
                

    static-keyword

    Detects:
  • global variables in headers (may cause problems with initialization ordering)
  • declarations of functions, local for translation unit
  • // file.h
    #pragma once
    inline static int a; // warning
    namespace { int b; } // warning
    // file.cpp
    static int c = 42; // will probably be judged, but ok
    static void local_func() {} // warning
                

    return-value-type

    Detects:
  • if function returns `const T`, suggests returning either `const T &` or `T` instead
  • if member function return type is `T`, but it only returns `*this`, suggests using `const T &`
  • const int foo() { return 42; } // warning
    struct Iterator
    {
        Iterator operator ++ ()
        {
            inc();
            return *this;
        }
    private:
        void inc();
    };