Каким-то образом нам довелось проверить большинство библиотек, составляющих коллекцию под названием «Awesome hpp». Это небольшие заголовочные проекты на C++. Надеемся, информация о найденных ошибках поможет сделать библиотеки лучше. Мы также будем рады видеть, что разработчики используют PVS-Studio на регулярной основе благодаря предоставляемой нами опции бесплатной лицензии.
Далее следует обзор ошибок, обнаруженных в различных библиотеках из тщательно отобранного списка замечательных библиотек C++, состоящих только из заголовков: awesome-hpp.
Об этом списке я узнал из подкаста Кроссплатформенная мобильная телефония. Пока мы на этом, я рекомендую всем программистам C++ проверить CppCast. Это первый подкаст для разработчиков C++ от разработчиков C++!
Несмотря на большое количество проектов, составляющих список, багов было немного. Тому есть три причины:
- Проекты крошечные. Многие буквально состоят только из заголовочного файла.
- Мы решили пропустить некоторые проекты, так как их было слишком сложно компилировать.
- Часто вы не можете выяснить, есть ли в шаблонных классах/функциях ошибки, пока не создадите их экземпляры. Следовательно, многие ошибки можно обнаружить только в реальном проекте, активно использующем библиотеку. Что касается нашего метода анализа, то мы просто включили заголовки в пустой .cpp файл и запустили проверку, что резко ограничило ее эффективность.
Тем не менее, мы собрали достаточно предупреждений, чтобы написать статью, которую вы сейчас читаете, и пару дополнительных.
Примечание для моих товарищей по команде :). Когда я что-то делаю, мне нравится ставить и достигать сразу несколько целей, и я призываю вас последовать моему примеру. Узнав о коллекции awesome-hpp, мне удалось выполнить следующие полезные задачи:
- поставить перед нашей командой задачу включить в нашу тестовую базу несколько хороших небольших современных проектов, основанных на стандартах C++11, C++14 и C++17;
- написал статью Почему важно применять статический анализ для открытых библиотек, которые вы добавляете в свой проект;
- написал статью Почему обзоры кода — это хорошо, но недостаточно;
- написал эту статью;
- наметил будущий пост о порядке оценки аргументов (см. Парсер CSV);
- нашел способ узнать, кто из моих товарищей по команде внимательно читает мои сообщения. Если вы это читали, заходите и получайте приз — плитку шоколада или что-то в этом роде :);
- продемонстрировал на этом примере, как выполнить несколько задач.
Примечание для разработчиков библиотек. Вы можете использовать PVS-Studio для бесплатной проверки проектов с открытым исходным кодом. Чтобы получить бесплатную лицензию на использование с вашим проектом с открытым исходным кодом, пожалуйста, заполните эту форму.
Хорошо, давайте перейдем к нашему обзору.
Найдены ошибки
лучший
Краткое описание библиотеки iutest:
iutest — это платформа для написания тестов C++.
template<typename Event>
pool_handler<Event> & assure() {
....
return static_cast<pool_handler<Event> &>(it == pools.cend() ?
*pools.emplace_back(new pool_handler<Event>{}) : **it);
....
}
Диагностическое сообщение PVS-Studio: V1023 В контейнер pools методом emplace_back добавлен указатель без владельца. Утечка памяти произойдет в случае исключения. entt.hpp 17114
Этот код может привести к утечке памяти. Если контейнеру требуется перераспределение и ему не удается выделить память для нового массива, он выдаст исключение и указатель будет потерян.
Ну, при обнаружении в тестах такого рода баг не очень критичен и скорее всего возникнет, но я все же должен был упомянуть его в образовательных целях :).
Фиксированный код:
pools.emplace_back(std::make_unique<pool_handler<Event>>{})
Еще одно проблемное место: V1023 В контейнер pools методом emplace_back добавляется указатель без владельца. Утечка памяти произойдет в случае исключения. entt.hpp 17407
jsoncons
Краткое описание библиотеки jsoncons:
Библиотека C++, содержащая только заголовки, для создания форматов данных, подобных JSON и JSON, с указателем JSON, исправлением JSON, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.
Ошибка 1
static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8;
uint64_t* data() { return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; }
basic_bigint& operator<<=( uint64_t k ) { size_type q = (size_type)(k / basic_type_bits); .... if ( k ) // 0 < k < basic_type_bits: { uint64_t k1 = basic_type_bits - k; uint64_t mask = (1 << k) - 1; // <= .... data()[i] |= (data()[i-1] >> k1) & mask; .... } reduce(); return *this; }
Диагностическое сообщение PVS-Studio: V629 Попробуйте проверить выражение 1 ‹‹ k. Битовый сдвиг 32-битного значения с последующим расширением до 64-битного типа. bigint.hpp 744
Этот баг уже подробно обсуждался в статье «Почему важно применять статический анализ для открытых библиотек, которые вы добавляете в свой проект». В двух словах, чтобы получить правильные значения маски, вы должны написать следующее:
uint64_t mask = (static_cast<uint64_t>(1) << k) - 1;
Вот альтернативная версия:
uint64_t mask = (1ull << k) - 1;
Еще одна похожая ошибка была обнаружена здесь: V629. Рассмотрим проверку выражения 1 ‹‹ k. Битовый сдвиг 32-битного значения с последующим расширением до 64-битного типа. bigint.hpp 779
Ошибка 2
template <class CharT = typename std::iterator_traits<Iterator>::value_type>
typename std::enable_if<sizeof(CharT) == sizeof(uint16_t)>::type
next() UNICONS_NOEXCEPT
{
begin_ += length_;
if (begin_ != last_)
{
if (begin_ != last_)
{
....
}
Диагностическое сообщение PVS-Studio: V571 Периодическая проверка. Условие if (begin_ != last_) уже проверено в строке 1138. unicode_traits.hpp 1140
Это странная двойная проверка. Я подозреваю, что второе условие содержит опечатку и предназначалось для проверки какого-то другого значения.
зажим
Краткое описание библиотеки clipp:
clipp — интерфейсы командной строки для современного C++. Простая в использовании, мощная и выразительная обработка аргументов командной строки для C++ 11/14/17, содержащаяся в одном заголовочном файле.
inline bool
fwd_to_unsigned_int(const char*& s)
{
if(!s) return false;
for(; std::isspace(*s); ++s);
if(!s[0] || s[0] == '-') return false;
if(s[0] == '-') return false;
return true;
}
Диагностическое сообщение PVS-Studio: V547 Выражение ‘s[0] == ‘-’’ всегда ложно. clipp.h 303
На самом деле это не ошибка — просто избыточный код. Элемент дважды проверяется на наличие знака минус.
SimpleIni
Краткое описание библиотеки SimpleIni:
Кроссплатформенная библиотека, предоставляющая простой API для чтения и записи файлов конфигурации в стиле INI. Он поддерживает файлы данных в форматах ASCII, MBCS и Unicode.
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
Диагностическое сообщение PVS-Studio: V1040 Возможна опечатка в написании предопределенного имени макроса. Макрос _linux похож на __linux. SimpleIni.h 2923
Кажется, в имени макроса _linux отсутствует символ подчеркивания: __linux. В любом случае, этот макрос устарел в POSIX, поэтому вместо него следует использовать __linux__.
Парсер CSV
Краткое описание библиотеки CSV Parser:
Современная библиотека C++ для чтения, записи и анализа файлов CSV (и аналогичных файлов).
CSV_INLINE void CSVReader::read_csv(const size_t& bytes) {
const size_t BUFFER_UPPER_LIMIT = std::min(bytes, (size_t)1000000);
std::unique_ptr<char[]> buffer(new char[BUFFER_UPPER_LIMIT]);
auto * HEDLEY_RESTRICT line_buffer = buffer.get();
line_buffer[0] = '\0';
....
this->feed_state->feed_buffer.push_back(
std::make_pair<>(std::move(buffer), line_buffer - buffer.get())); // <=
....
}
Диагностическое сообщение PVS-Studio: V769 Указатель buffer.get() в выражении line_buffer — buffer.get() равен nullptr. Полученное значение бессмысленно и не должно использоваться. csv.hpp 4957
Случай интересный, требующий тщательного расследования, поэтому я решил написать о нем небольшой отдельный пост. К тому же, экспериментируя с подобным кодом, я обнаружил недоработку в коде самого PVS-Studio :). Из-за этого недостатка анализатор иногда молчит, когда должен выдать предупреждение.
Короче говоря, будет ли работать этот код, зависит от порядка вычисления аргументов, а это зависит от компилятора.
PПечать
Краткое описание библиотеки PPrint:
Красивый принтер для современного C++.
template <typename Container>
typename std::enable_if<......>::type print_internal(......) {
....
for (size_t i = 1; i < value.size() - 1; i++) {
print_internal(value[i], indent + indent_, "", level + 1);
if (is_container<T>::value == false)
print_internal_without_quotes(", ", 0, "\n");
else
print_internal_without_quotes(", ", 0, "\n");
}
....
}
Диагностическое сообщение PVS-Studio: V523 Оператор then эквивалентен оператору else. pprint.hpp 715
Очень странно, что одна и та же логика выполняется независимо от результата проверки. Уточняющий комментарий также не приводится. Это очень похоже на ошибку копирования-вставки.
Похожие предупреждения:
- V523 Оператор "then" эквивалентен оператору "else". pprint.hpp 780
- V523 Оператор "then" эквивалентен оператору "else". pprint.hpp 851
- V523 Оператор "then" эквивалентен оператору "else". pprint.hpp 927
- V523 Оператор "then" эквивалентен оператору "else". pprint.hpp 1012
Стрф
Краткое описание библиотеки Strf:
Быстрая библиотека форматирования C++, поддерживающая преобразование кодировки.
Ошибка 1
template <int Base>
class numpunct: private strf::digits_grouping
{
....
constexpr STRF_HD numpunct& operator=(const numpunct& other) noexcept
{
strf::digits_grouping::operator=(other);
decimal_point_ = other.decimal_point_;
thousands_sep_ = other.thousands_sep_;
}
....
};
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. numpunct.hpp 402
«Вернуть *это;» заявление отсутствует в конце.
Ошибка 2 (того же характера)
template <int Base>
class no_grouping final
{
constexpr STRF_HD no_grouping& operator=(const no_grouping& other) noexcept
{
decimal_point_ = other.decimal_point_;
}
....
}
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. numpunct.hpp 528.
Индикаторы
Краткое описание библиотеки Индикаторы:
Индикаторы активности для современного C++.
static inline void move_up(int lines) { move(0, -lines); }
static inline void move_down(int lines) { move(0, -lines); } // <=
static inline void move_right(int cols) { move(cols, 0); }
static inline void move_left(int cols) { move(-cols, 0); }
Диагностическое сообщение PVS-Studio: V524 Странно, что тело функции move_down полностью эквивалентно телу функции move_up. индикаторы.hpp 983
Я не уверен, что это ошибка, но код очень подозрительный. Разработчик, должно быть, скопировал функцию move_up и изменил имя копии на move_down, но забыл удалить знак минус. В любом случае, этот фрагмент нуждается в проверке.
Примечание. Если код правильный, вы должны понимать, что он запутает не только статические анализаторы, но и сторонних разработчиков, которые могут захотеть использовать или развивать его. Не забудьте оставить комментарий, чтобы уточнить свою точку зрения.
Маниф
Краткое описание библиотеки manif:
manif — это библиотека теории лжи C++11, состоящая только из заголовков, для оценки состояния, предназначенная для приложений робототехники.
template <typename _Derived> typename LieGroupBase<_Derived>::Scalar* LieGroupBase<_Derived>::data() { return derived().coeffs().data(); }
template <typename _Derived> const typename LieGroupBase<_Derived>::Scalar* LieGroupBase<_Derived>::data() const { derived().coeffs().data(); // <= }
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. ложь_группа_база.h 347
Неконстантная функция реализована правильно, а константная — нет. Интересно, как так получилось…
Притворяться
Краткое описание библиотеки FakeIt:
FakeIt – это простая фиктивная платформа для C++. Он поддерживает GCC, Clang и MS Visual C++. FakeIt написан на C++11 и может использоваться для тестирования проектов C++11 и C++.
template<typename ... arglist>
struct ArgumentsMatcherInvocationMatcher :
public ActualInvocation<arglist...>::Matcher {
....
template<typename A>
void operator()(int index, A &actualArg) {
TypedMatcher<typename naked_type<A>::type> *matcher =
dynamic_cast<TypedMatcher<typename naked_type<A>::type> *>(
_matchers[index]);
if (_matching)
_matching = matcher->matches(actualArg);
}
....
const std::vector<Destructible *> _matchers;
};
Диагностическое сообщение PVS-Studio: V522 Возможно, происходит разыменование потенциального нулевого указателя сопоставление. подделка.hpp 6720
Указатель matcher инициализируется значением, возвращаемым dynamic_cast. Однако этот оператор может вернуть nullptr, что очень вероятно. В противном случае используйте более эффективное static_cast вместо dynamic_cast.
Я подозреваю, что условие содержит опечатку и на самом деле должно было выглядеть так:
if (matcher)
_matching = matcher->matches(actualArg);
Гуилайт
Краткое описание библиотеки GuiLite:
Самая маленькая библиотека GUI, состоящая только из заголовков (4 KLOC) для всех платформ.
#define CORRECT(x, high_limit, low_limit) {\ x = (x > high_limit) ? high_limit : x;\ x = (x < low_limit) ? low_limit : x;\ }while(0)
void refresh_wave(unsigned char frame) { .... CORRECT(y_min, m_wave_bottom, m_wave_top); .... }
Диагностическое сообщение PVS-Studio: V529 Нечетная точка с запятой ; после оператора пока. GuiLite.h 3413
Эта ошибка в макросе не вызывает какой-то конкретной проблемы, но это все равно ошибка, поэтому я ее включил.
Макрос должен был использовать классический шаблон do { …. } пока(….). Это позволяет выполнять несколько операций в одном блоке, при этом позволяя вам написать красивую точку с запятой после макроса, как если бы это был вызов функции.
Однако в этом макросе отсутствует ключевое слово do. В результате он распадается, так сказать, на две части: блок кода и пустой, никогда не выполняющийся while(0); петля.
Но почему это плохо, на самом деле?
Ну, во-первых, вы не можете использовать этот макрос в таких конструкциях:
if (A)
CORRECT(y_min, m_wave_bottom, m_wave_top);
else
Foo();
Этот код не будет компилироваться, так как он расширится до следующего:
if (A)
{ ..... }
while(0);
else
Foo();
Думаю, вы согласитесь, что такие дефекты лучше находить и исправлять, пока библиотека еще находится в разработке, чем после релиза. Чтобы убедиться в этом, используйте статический анализ :).
PPLUX
Краткое описание библиотеки PpluX:
Библиотеки C++ с одним заголовком для планирования потоков, рендеринга и т. д.
struct DisplayList {
DisplayList& operator=(DisplayList &&d) {
data_ = d.data_;
d.data_ = nullptr;
}
....
}
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. px_render.h 398
Универсальный
Краткое описание универсальной библиотеки:
Цель Universal Numbers, или unums, – заменить систему счисления с плавающей запятой IEEE более эффективной и математически последовательной в средах параллельного выполнения.
Ошибка 1
template<typename Scalar>
vector<Scalar> operator*(double scalar, const vector<Scalar>& v) {
vector<Scalar> scaledVector(v);
scaledVector *= scalar;
return v;
}
Диагностическое сообщение PVS-Studio: V1001 Переменная scaledVector назначена, но к концу функции не используется. вектор.hpp 124
Это опечатка. Функция должна возвращать новый вектор scaledVector, а не исходный вектор v.
Еще одна похожая опечатка: V1001 Переменная «normalizedVector» присваивается, но не используется к концу функции. вектор.hpp 131
Ошибка 2
template<typename Scalar>
class matrix {
....
matrix& diagonal() {
}
....
};
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. матрица.hpp 109
Ошибка 3
template<size_t fbits, size_t abits> void module_subtract_BROKEN( const value<fbits>& lhs, const value<fbits>& rhs, value<abits + 1>& result) { if (lhs.isinf() || rhs.isinf()) { result.setinf(); return; } int lhs_scale = lhs.scale(), rhs_scale = rhs.scale(), scale_of_result = std::max(lhs_scale, rhs_scale);
// align the fractions bitblock<abits> r1 = lhs.template nshift<abits>(lhs_scale - scale_of_result + 3); bitblock<abits> r2 = rhs.template nshift<abits>(rhs_scale - scale_of_result + 3); bool r1_sign = lhs.sign(), r2_sign = rhs.sign(); //bool signs_are_equal = r1_sign == r2_sign;
if (r1_sign) r1 = twos_complement(r1); if (r1_sign) r2 = twos_complement(r2); // <=
.... }
Диагностическое сообщение PVS-Studio: V581 Условные выражения операторов if, расположенных рядом, идентичны. Проверить строки: 789, 790. value.hpp 790
Это классическая ошибка копирования-вставки. Программист клонировал следующую строку:
if (r1_sign) r1 = twos_complement(r1);
Изменено r1 на r2:
if (r1_sign) r2 = twos_complement(r2);
Но забыл изменить r1_sign. Вот правильная версия:
if (r2_sign) r2 = twos_complement(r2);
Библиотеки с одним заголовком Chobo
Краткое описание библиотеки Chobo Single-Header Libraries:
Набор небольших однозаголовочных библиотек C++11 от Chobolabs.
Ошибка 1
template <typename T, typename U, typename Alloc = std::allocator<T>>
class vector_view
{
....
vector_view& operator=(vector_view&& other)
{
m_vector = std::move(other.m_vector);
}
....
}
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. vector_view.hpp 163
Ошибка 2
template <typename UAlloc>
vector_view& operator=(const std::vector<U, UAlloc>& other)
{
size_type n = other.size();
resize(n);
for (size_type i = 0; i < n; ++i)
{
this->at(i) = other[i];
}
}
Диагностическое сообщение PVS-Studio: V591 Непустая функция должна возвращать значение. vector_view.hpp 184
PGM-индекс
Краткое описание библиотеки PGM-индекс:
Индекс кусочно-геометрической модели (PGM-индекс) – это структура данных, которая обеспечивает быстрый поиск, поиск предшественника, поиск по диапазону и обновление в массивах из миллиардов элементов, используя на несколько порядков меньше места, чем традиционные индексы, обеспечивая при этом тот же наихудший случай. гарантии времени запроса.
Ошибка 1
char* str_from_errno()
{
#ifdef MSVC_COMPILER
#pragma warning(disable:4996)
return strerror(errno);
#pragma warning(default:4996)
#else
return strerror(errno);
#endif
}
Диагностическое сообщение PVS-Studio: V665 Возможно, использование #pragma warning(default: X) в данном контексте некорректно. Вместо этого следует использовать предупреждение #pragma (push/pop). Проверить строки: 9170, 9172. sdsl.hpp 9172
Этот код временно отключает предупреждение компилятора, но делает это некорректно. Такие ошибки можно допустить в пользовательском коде, но уж точно не в библиотеках только для заголовков.
Ошибка 2
template<class t_int_vec>
t_int_vec rnd_positions(uint8_t log_s, uint64_t& mask,
uint64_t mod=0, uint64_t seed=17)
{
mask = (1<<log_s)-1; // <=
t_int_vec rands(1<<log_s ,0);
set_random_bits(rands, seed);
if (mod > 0) {
util::mod(rands, mod);
}
return rands;
}
Диагностическое сообщение PVS-Studio: V629 Попробуйте проверить выражение ‘1 ‹‹ log_s’. Битовый сдвиг 32-битного значения с последующим расширением до 64-битного типа. sdsl.hpp 1350
Вот одна правильная версия:
mask = ((uint64_t)(1)<<log_s)-1;
Хнсвлиб
Краткое описание библиотеки Hnswlib:
Реализация C++ HNSW только для заголовков с привязками python. Код бумаги для эксперимента HNSW 200M SIFT.
template<typename dist_t>
class BruteforceSearch : public AlgorithmInterface<dist_t> {
public:
BruteforceSearch(SpaceInterface <dist_t> *s, size_t maxElements) {
maxelements_ = maxElements;
data_size_ = s->get_data_size();
fstdistfunc_ = s->get_dist_func();
dist_func_param_ = s->get_dist_func_param();
size_per_element_ = data_size_ + sizeof(labeltype);
data_ = (char *) malloc(maxElements * size_per_element_);
if (data_ == nullptr)
std::runtime_error(
"Not enough memory: BruteforceSearch failed to allocate data");
cur_element_count = 0;
}
....
}
Диагностическое сообщение PVS-Studio: V596 Объект создан, но не используется. Ключевое слово throw может отсутствовать: throw runtime_error(FOO); брутфорс.ч 26
Отсутствует оператор throw перед std::runtime_error.
Аналогичная проблема: V596 Объект создан, но не используется. Ключевое слово throw может отсутствовать: throw runtime_error(FOO); брутфорс.ч 161
крошечный днн
Краткое описание библиотеки tiny-dnn:
tiny-dnn — это реализация глубокого обучения на C++14. Он подходит для глубокого обучения на ограниченных вычислительных ресурсах, встроенных системах и устройствах Интернета вещей.
Ошибка 1
class nn_error : public std::exception { public: explicit nn_error(const std::string &msg) : msg_(msg) {} const char *what() const throw() override { return msg_.c_str(); }
private: std::string msg_; };
inline Device::Device(device_t type, const int platform_id, const int device_id) : type_(type), has_clcuda_api_(true), platform_id_(platform_id), device_id_(device_id) { .... #else nn_error("TinyDNN has not been compiled with OpenCL or CUDA support."); #endif }
Диагностическое сообщение PVS-Studio: V596 Объект создан, но не используется. Ключевое слово throw может отсутствовать: throw nn_error(FOO); устройство.ч 68
nn_error — это не функция, генерирующая исключение, а просто класс, и правильный способ его использования выглядит следующим образом:
throw nn_error("TinyDNN has not been compiled with OpenCL or CUDA support.");
Вот еще один случай неправильного использования этого класса: V596 Объект создан, но не используется. Ключевое слово throw может отсутствовать: throw nn_error(FOO); conv2d_op_opencl.h 136
Ошибка 2
inline std::string format_str(const char *fmt, ...) { static char buf[2048];
#ifdef _MSC_VER #pragma warning(disable : 4996) #endif va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); #ifdef _MSC_VER #pragma warning(default : 4996) #endif return std::string(buf); }
Диагностическое сообщение PVS-Studio: V665 Возможно, использование #pragma warning(default: X) в данном контексте некорректно. Вместо этого следует использовать предупреждение #pragma (push/pop). Контрольные строки: 139, 146. утил.ч 146
Длиб
Краткое описание библиотеки Dlib:
Dlib — это современный набор инструментов C++, содержащий алгоритмы машинного обучения и инструменты для создания сложного программного обеспечения на C++ для решения реальных задач.
Ошибка 1
Чтобы было интересно, предлагаю вам найти ошибку самостоятельно.
class bdf_parser { public:
enum bdf_enums { NO_KEYWORD = 0, STARTFONT = 1, FONTBOUNDINGBOX = 2, DWIDTH = 4, DEFAULT_CHAR = 8, CHARS = 16, STARTCHAR = 32, ENCODING = 64, BBX = 128, BITMAP = 256, ENDCHAR = 512, ENDFONT = 1024 }; .... bool parse_header( header_info& info ) { .... while ( 1 ) { res = find_keywords( find | stop ); if ( res & FONTBOUNDINGBOX ) { in_ >> info.FBBx >> info.FBBy >> info.Xoff >> info.Yoff; if ( in_.fail() ) return false; // parse_error find &= ~FONTBOUNDINGBOX; continue; } if ( res & DWIDTH ) { in_ >> info.dwx0 >> info.dwy0; if ( in_.fail() ) return false; // parse_error find &= ~DWIDTH; info.has_global_dw = true; continue; } if ( res & DEFAULT_CHAR ) { in_ >> info.default_char; if ( in_.fail() ) return false; // parse_error find &= ~DEFAULT_CHAR; continue; } if ( res & NO_KEYWORD ) return false; // parse_error: unexpected EOF break; } .... };
При удаче?
Вот:
if ( res & NO_KEYWORD )
Диагностическое сообщение PVS-Studio: V616 В побитовой операции используется именованная константа NO_KEYWORD со значением 0. шрифты.cpp 288
Значение именованной константы NO_KEYWORD равно 0. Следовательно, условие не имеет смысла. Вот как должна выглядеть правильная проверка:
if ( res == NO_KEYWORD )
Еще одна неверная проверка: V616 В побитовой операции используется именованная константа NO_KEYWORD со значением 0. шрифты.cpp 334
Ошибка 2
void set(std::vector<tensor*> items)
{
....
epa.emplace_back(new enable_peer_access(*g[0], *g[i]));
....
}
Диагностическое сообщение PVS-Studio: V1023 В контейнер epa методом emplace_back добавлен указатель без владельца. Утечка памяти произойдет в случае исключения. tensor_tools.h 1665
Чтобы разобраться, что здесь не так, смотрите документацию V1023.
Ошибка 3
template < typename detection_type, typename label_type > bool is_track_association_problem ( const std::vector< std::vector<labeled_detection<detection_type,label_type> > >& samples ) { if (samples.size() == 0) return false;
unsigned long num_nonzero_elements = 0; for (unsigned long i = 0; i < samples.size(); ++i) { if (samples.size() > 0) ++num_nonzero_elements; } if (num_nonzero_elements < 2) return false; .... }
Диагностическое сообщение PVS-Studio: V547 Выражение samples.size() › 0 всегда истинно. свм.ч 360
Это очень, очень странный фрагмент кода! Если цикл начинается, то условие (samples.size() › 0) всегда выполняется. А это значит, что цикл можно упростить:
for (unsigned long i = 0; i < samples.size(); ++i)
{
++num_nonzero_elements;
}
Но теперь становится ясно, что петля вам вообще не нужна. Фрагмент можно было бы переписать намного проще и эффективнее:
unsigned long num_nonzero_elements = samples.size();
Но было ли это намерением разработчика? Этот код, безусловно, нуждается в тщательной проверке.
Ошибка 4
class console_progress_indicator { .... double seen_first_val; .... };
bool console_progress_indicator::print_status ( double cur, bool always_print) { .... if (!seen_first_val) { start_time = cur_time; last_time = cur_time; first_val = cur; seen_first_val = true; // <= return false; } .... }
Диагностическое сообщение PVS-Studio: V601 Тип bool неявно приводится к типу double. console_progress_indicator.h 136
Значение true сохраняется в члене класса типа double. Хм….
Ошибка 5
void file::init(const std::string& name)
{
....
WIN32_FIND_DATAA data;
HANDLE ffind = FindFirstFileA(state.full_name.c_str(), &data);
if (ffind == INVALID_HANDLE_VALUE ||
(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0)
{
throw file_not_found("Unable to find file " + name);
}
else
{
....
}
}
Диагностическое сообщение PVS-Studio: V773 Исключение было сгенерировано без закрытия файла, на который ссылается дескриптор ffind. Возможна утечка ресурсов. dir_nav_kernel_1.cpp 60
Исключение выдается, если каталог найден. Но как насчет закрытия дескриптора файла?
Ошибка 6
Еще одно крайне странное место.
inline double poly_min_extrap(double f0, double d0, double x1, double f_x1, double x2, double f_x2) { .... matrix<double,2,2> m; matrix<double,2,1> v;
const double aa2 = x2*x2; const double aa1 = x1*x1; m = aa2, -aa1, -aa2*x2, aa1*x1; v = f_x1 - f0 - d0*x1, f_x2 - f0 - d0*x2; .... }
Диагностическое сообщение PVS-Studio: V521 Такие выражения с оператором , опасны. Убедитесь, что выражение правильное. оптимизация_line_search.h 211
План состоял в том, чтобы инициализировать матрицы. Но все эти aa2, f_x1, d0 и т. д. — это просто переменные типа double. А это значит, что запятые не разделяют аргументы, которые используются для формирования матриц, а просто действуют как оператор запятая, который возвращает значение правого операнда.
Вывод
В начале этой статьи я привел вам пример того, как можно преследовать сразу несколько целей. Использование статического анализатора также выгодно по нескольким причинам:
- Это поможет вам повысить свою квалификацию. Вы узнаете много нового и полезного, работая с предупреждениями анализатора. Например, см. memset, #pragma warning, emplace_back, strictlyaligned.
- Опечатки, ошибки и потенциальные уязвимости обнаруживаются на более ранних этапах разработки.
- Код постепенно становится более качественным, простым и понятным.
- Вы можете гордиться и хвастаться использованием современных технологий в разработке программного обеспечения :). Ну, это только полушутка. Статический анализ действительно дает вам значительное конкурентное преимущество.
Остается только один вопрос: как начать работу со статическим анализом, плавно интегрировать его и правильно использовать. Следующие статьи дадут вам ответы на все вопросы: