
Если регулярно использовать статический анализатор кода, то можно сократить время на гадание, почему новый код работает как-то не так, как задумывалось. Рассмотрим очередную интересную ошибку, когда в процессе рефакторинга сломалась функция и это осталось не замеченным человеком.
Думал сделать перерыв в серии маленьких заметок про то, как анализатор PVS-Studio оперативно находит ошибки в новом коде. Но, увидев в почте отчёт от системы автоматической проверки проекта Blender, не выдержал. Предлагаю вашему вниманию очередную простую, но именно из-за этого красивую ошибку.
Итак, жил-был вот такой код для обработки вектора значений. Суть — не дать значениям выходить за определённый диапазон.
#define CLAMP(a, b, c) \ { \ if ((a) < (b)) { \ (a) = (b); \ } \ else if ((a) > (c)) { \ (a) = (c); \ } \ } \ (void)0 template <typename T> inline T clamp(const T &a, const bT &min_v, const bT &max_v) { T result = a; for (int i = 0; i < T::type_length; i++) { CLAMP(result[i], min_v, max_v); } return result; }
И было всё хорошо. А потом программист решил, что нет смысла использовать самодельный макрос CLAMP, а лучше воспользоваться стандартной функцией std::clamp. И в коммите, призванном сделать мир лучше, код стал таким:
template <typename T, int Size> inline vec_base<T, Size> clamp(const vec_base<T, Size> &a, const T &min, const T &max) { vec_base<T, Size> result = a; for (int i = 0; i < Size; i++) { std::clamp(result[i], min, max); } return result; }
Вот только поспешил. Видите ошибку? Возможно, видите, возможно, нет. В любом случае, программист, написавший этот код, явно не заметил, что код сломался.
Зато дотошный статический анализатор кода PVS-Studio тут как тут с сообщением:
[CWE-252] V530: The return value of function ‘clamp’ is required to be utilized. BLI_math_vector.hh 88
Дело в том, что функция std::clamp не меняет значение элемента в контейнере:
template <class T> constexpr const T& clamp( const T& v, const T& lo, const T& hi );
Макрос CLAMP раньше изменял значение, а стандартная функция нет. Поэтому теперь код сломан и ждёт, когда кто-то заметит проявление ошибки и пойдёт искать её причину. Такую ошибку можно было бы заметить и исправить ещё на этапе написания кода, если бы использовался PVS-Studio. Не будьте как разработчики Blender :). Используйте статический анализ кода на регулярной основе. И вы сэкономите свои силы и время.
Примечание. Кстати, рядом в коде есть ещё одно неправильное использование std::clamp.
Правильный вариант кода:
template <typename T, int Size> inline vec_base<T, Size> clamp(const vec_base<T, Size> &a, const T &min, const T &max) { vec_base<T, Size> result = a; for (int i = 0; i < Size; i++) { result[i] = std::clamp(result[i], min, max); } return result; }
Спасибо за внимание. И, если ещё не видели, заглядывайте почитать про топ-10 ошибок в C++ открытых проектах, которые мы нашли за 2021 год.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. How PVS-Studio prevents rash code changes, example N4.
ссылка на оригинал статьи https://habr.com/ru/company/pvs-studio/blog/652383/
Добавить комментарий