Просто не получится :). Это ведь ещё и разные диагностики :). Изначально речь шла про V634. Она именно оценивает выражения со сдвигами.
Паттерн вида x << y + z.
Забывают, что у сложения/вычитания/умножения/деления более высокий приоритет, чем у сдвига. Лучше всегда явно ставить скобки.
Пример:
int x = a<<4 + b;
Исключения:
Операция с высоким приоритетом находится в макросе, а сдвиг нет.
Операция с высоким приоритетом ('-','/','%') выполняется над числом 64(int64), 32(int), 16(short), 8(char).
Разделение на строки выражения свидетельствует о преднамеренном использовании:
a = 1 <<
2 * 8; //ok
b = 2
<< 2 * 8 //ok
c = 3 << 2
* 8 //err
Нет смысл сдвигать вправо константу на константу или влево константу (кроме 1) на константу.
Сдвиг является условием. В этом случае нет смысла результат сдвига умножать. Пример: if (value >> (i — 1) * 8)
А если речь заходит про && и ||, то это уже V648.
Странно, когда логические операции '&&' и '||' используются вместе и не разделены скобками. Часто их приоритеты путают. Приоритет оператора && выше, чем у ||.
Следует предупреждать в том случае, если написано так: A || B && C.
Исключение:
Если имеется выражение вида A || !A && B, т.е. оператор || разделяет два противоположных по смыслу высказывания.
Если выражение вида… || p != nullptr && f(p) ...
Имеется выражение вида x == 0 || A != 123 && A != 321, т.е. оператор &&
разделяет сравнение одной и той же переменной с разными константами.
При этом, с одной стороны от последовательности || && ничего не должно быть.
Т.е. здесь не ругаемся: b || A != 0 && A != 1
а здесь ругаемся: b || A != 0 && A != 1 || c
и здесь ругаемся: b || A != 0 && (A != 1 || c)
И правильно что ругается, ибо каждый раз нужно вспоминать (или подглядывать) приоритет операций. Если лишнее — эту диагностику можно отключить. В данном случае ситуация ещё усугубляется повторяющимися скобками.
Как я понимаю, варианты бесплатного лицензирования, описанные в этой статье, не подходят по той причине, что Вы делаете маленькие проекты на заказ? Но тогда и проблемы как таковой нет. В маленьких проектах низкая плотность ошибок, и статический анализ не столь актуален, как в больших проектах. Ну или по крайней мере можно использовать более простыми бесплатными анализаторами, такими как Cppcheck.
Если проект большой и над ним работает команда, то не вижу препятствия запросить у заказчика приобретение лицензии.
И началась эпическая переписка с техподдержкой, которая – исключительно по моей вине – растянулась почти на год (!). Вот честное слово — техподдержка у PVS-Studio – натурально лучшая из всех, с кем я общался, а общался я со многими, от российских производителей микросхем, где человек поздравлял меня с «днём пирожков с малиновым вареньем» (нет, это не шутка) до крупнейших зарубежных компаний, где меня месяцами футболили от человека к человеку :)
Тут же я со стыдом признаюсь, что отвечал существенно медленнее, чем отвечали мне… частично меня оправдывает необходимость заниматься основными рабочими задачами, но только частично.
Сам по себе вызов виртуального метода 'foo' в конструкторе класса 'Base' может не являться ошибкой, однако проблемы могут проявить себя в производных классах.
Во время создания объекта типа 'Child' будет вызван метод 'Base::foo()' из конструктора базового класса, но не переопределенный метод 'Child::foo()' из производного класса. Отметим, что в некоторых других языках программирования (C#, Java, ...) аналогичный код будет работать иначе: во время создания объекта 'Child' конструктором по умолчанию будет вызван конструктор по умолчанию базового класса 'Base', который затем вызовет переопределенный метод 'Child::foo()'.
Для исправления этой проблемы нужно уточнить вызов метода. Например, для класса 'Base':
Теперь одного взгляда на код достаточно чтобы понять, какой именно метод будет вызван.
Также отметим, что использование указателя на себя 'this' при вызове виртуального метода не решает исходную проблему, и при использовании 'this' все также необходимо уточнить, из какого класса следует позвать виртуальную функцию:
struct Base
{
Base()
{
this->foo(); // bad
this->Base::foo(); // good
}
virtual ~Base() = default;
virtual void foo() const;
};
Мне совершенно не нравится трактовка "потенциально опасны". В чем же опасны, если конвертация идет только в 0 или 1?
Опасна не сама конвертация. Опасна аномальность этого действия.
Окей, допустим это ошибка, и хотели логическое ИЛИ - известный прием записи в булевые переменные результатов условий.
Любой практикующий С или С++ программист понимает, что здесь совсем другое. Видно, что хотели использовать именно побитовое ИЛИ, чтобы сформировать маску из разных бит. Обратите внимание, что именованные константы в enum являются степенями двойки.
Итак, с помощью | составляется маска из битов. Далее эту маску хотят записать в переменную типа bt_proximity_property_t. Это не очень красиво, но допустимо, так как для представления enum используется достаточное количество бит для вмещения значения 3. Подробнее про тонкости.
Но точно нет никакого смысла запихивать маску b0..0011 в переменную типа bool. Это аномалия. Да, этот код компилируется. Да, понятно, как работает неявное приведение типов. Даже можно придумать какую-то ситуацию, когда такой код оправдан. Но всё равно это аномалия, о которой сообщает анализатор. И действительно - перед нами не какая-то хитрая задумка, а опечатка.
Мы посмотрели на этот ControlFlag, думали может по него какую-то статью обзорную написать. Но там писать не про что. Для кода анализатора PVS-Studio (C++ ядро) выдал всего два бессмысленных сообщения вида:
Ему подозрительно, что 2 соседних if'а имеют return true, а посередине return false. Хм…
Для сравнения, от того-же ASan, на практике гораздо больше прока: Зачем нужен динамический анализ кода, на примере проекта PVS-Studio.
PVS-Studio не ориентируется на ФСТЭК. Svace ориентирован на ФСТЭК. Актуален ФСТЭК - берите Svace.
Подразумевается, что каждый подписанный на C \ С++ хаб, слышал про Cppcheck :)
Паттерн вида x << y + z.
Забывают, что у сложения/вычитания/умножения/деления более высокий приоритет, чем у сдвига. Лучше всегда явно ставить скобки.
Пример:
Исключения:
А если речь заходит про && и ||, то это уже V648.
Странно, когда логические операции '&&' и '||' используются вместе и не разделены скобками. Часто их приоритеты путают. Приоритет оператора && выше, чем у ||.
Следует предупреждать в том случае, если написано так: A || B && C.
Исключение:
разделяет сравнение одной и той же переменной с разными константами.
При этом, с одной стороны от последовательности || && ничего не должно быть.
Если проект большой и над ним работает команда, то не вижу препятствия запросить у заказчика приобретение лицензии.
Из статьи "Интеграция PVS-Studio в uVision Keil":
Уж 10 лет как пишут: из 2011 - Народ против PVS-Studio: дубль первый (и последний :)
Аргументировать прочитанной (одной?) книгой, это конечно прикольно... Но к теме выявления потенциальных ошибок, это не имеет никакого отношения.
Приведу в свою очередь описание предупреждения V1053.
Анализатор обнаружил вызов виртуальной функции в конструкторе или деструкторе класса.
Рассмотрим пример:
Сам по себе вызов виртуального метода 'foo' в конструкторе класса 'Base' может не являться ошибкой, однако проблемы могут проявить себя в производных классах.
Во время создания объекта типа 'Child' будет вызван метод 'Base::foo()' из конструктора базового класса, но не переопределенный метод 'Child::foo()' из производного класса. Отметим, что в некоторых других языках программирования (C#, Java, ...) аналогичный код будет работать иначе: во время создания объекта 'Child' конструктором по умолчанию будет вызван конструктор по умолчанию базового класса 'Base', который затем вызовет переопределенный метод 'Child::foo()'.
Для исправления этой проблемы нужно уточнить вызов метода. Например, для класса 'Base':
Теперь одного взгляда на код достаточно чтобы понять, какой именно метод будет вызван.
Также отметим, что использование указателя на себя 'this' при вызове виртуального метода не решает исходную проблему, и при использовании 'this' все также необходимо уточнить, из какого класса следует позвать виртуальную функцию:
Данная диагностика классифицируется как: CERT-OOP50-CPP.
Опасна не сама конвертация. Опасна аномальность этого действия.
Любой практикующий С или С++ программист понимает, что здесь совсем другое. Видно, что хотели использовать именно побитовое ИЛИ, чтобы сформировать маску из разных бит. Обратите внимание, что именованные константы в enum являются степенями двойки.
Итак, с помощью | составляется маска из битов. Далее эту маску хотят записать в переменную типа bt_proximity_property_t. Это не очень красиво, но допустимо, так как для представления enum используется достаточное количество бит для вмещения значения 3. Подробнее про тонкости.
Но точно нет никакого смысла запихивать маску b0..0011 в переменную типа bool. Это аномалия. Да, этот код компилируется. Да, понятно, как работает неявное приведение типов. Даже можно придумать какую-то ситуацию, когда такой код оправдан. Но всё равно это аномалия, о которой сообщает анализатор. И действительно - перед нами не какая-то хитрая задумка, а опечатка.