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:
|
// 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:
|
void ok(const int & i) {}
void param_could_be_const(int & i) {} // warning
void rvalue_is_never_moved(int && i) {} // warning
|
improper-move |
Detects:
|
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:
|
// 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:
|
const int foo() { return 42; } // warning
struct Iterator
{
Iterator operator ++ ()
{
inc();
return *this;
}
private:
void inc();
};
|