Pull to refresh

Comments 23

Эмм.... А откуда исходники, версия? Я тут смотрю в поисках вот этого куска кода:

    (*variables)["set_has_field_bit_message"] = "";      // <=
    (*variables)["set_has_field_bit_message"] = "";      // <=

И не вижу его ни в одной версии исходников O_O

Ох. Мои глаза :D не в тот файл смотрю =) Просто строка-дубликат, не ошибка, пьфью.

Кажется там должно быть (*variables)["get_has_field_bit_message"]

Нет, там не предполагалось второй. Код вообще получен не копипастой, а редактированием - в первом случае дубликат убрали, во втором проглядели.

Последняя не ошибка. То, что анализатор будет ругаться на любое выражение типа
1 << 4*x
это странно.

Вне зависимости от того ошибка это или нет, это код который нетривиально парзить человеку.

Иногда это позволит найти какую-то ошибку.
С другой стороны, разбирать 100500 false positive тоже сомнительное удовольствие.
Не будет из 100500. Не верится, что такие выражения часто требуются. А если это особый проект, где их много, то можно отключить диагностику.
И правильно что ругается, ибо каждый раз нужно вспоминать (или подглядывать) приоритет операций. Если лишнее — эту диагностику можно отключить. В данном случае ситуация ещё усугубляется повторяющимися скобками.
А на это будет ругаться:
if (a && b || c && d) { ... }

А на это:
x = 1+3*y;

А почему такая разница?
if (a && b || c && d) // нет
if (a || b && c || d) // да
x = 1 + 3 * y; // нет

  1. Первая строка. Тот, кто писал, скорее всего понимает, что хочет объединить операцией || два подвыражения. Типовой код. Ошибки скорее всего нет.
  2. Вторая строка. Возможно, код работает не так, как ожидает программист. Возможно, ошибки нет, но код лучше перепроверить.
  3. Третья строка. Невероятно, что кто-то спутает приоритет операций + и *. Ошибки скорее всего нет.
А если
if (a || b && c)
?

Я просто правило хочу понять )))
Просто не получится :). Это ведь ещё и разные диагностики :). Изначально речь шла про V634. Она именно оценивает выражения со сдвигами.

Паттерн вида x << y + z.
Забывают, что у сложения/вычитания/умножения/деления более высокий приоритет, чем у сдвига. Лучше всегда явно ставить скобки.
Пример:
int x = a<<4 + b;

Исключения:
  1. Операция с высоким приоритетом находится в макросе, а сдвиг нет.
  2. Операция с высоким приоритетом ('-','/','%') выполняется над числом 64(int64), 32(int), 16(short), 8(char).
  3. Разделение на строки выражения свидетельствует о преднамеренном использовании:
     a = 1 <<
          2 * 8; //ok
     b = 2 
          << 2 * 8 //ok
     c = 3 << 2
              * 8 //err
    
  4. Нет смысл сдвигать вправо константу на константу или влево константу (кроме 1) на константу.
  5. Сдвиг является условием. В этом случае нет смысла результат сдвига умножать. Пример: if (value >> (i — 1) * 8)


А если речь заходит про && и ||, то это уже V648.

Странно, когда логические операции '&&' и '||' используются вместе и не разделены скобками. Часто их приоритеты путают. Приоритет оператора && выше, чем у ||.

Следует предупреждать в том случае, если написано так: A || B && C.
Исключение:
  1. Если имеется выражение вида A || !A && B, т.е. оператор || разделяет два противоположных по смыслу высказывания.
  2. Если выражение вида… || p != nullptr && f(p) ...
  3. Имеется выражение вида x == 0 || A != 123 && A != 321, т.е. оператор &&
    разделяет сравнение одной и той же переменной с разными константами.
    При этом, с одной стороны от последовательности || && ничего не должно быть.
       Т.е. здесь не ругаемся: b || A != 0 && A != 1
             а здесь ругаемся: b || A != 0 && A != 1 || c
             и здесь ругаемся: b || A != 0 && (A != 1 || c)
    

Понятно. Всякие очевидные false positive прописываются в исключения, поэтому в результате их будет немного. Плюс прогоны инструмента на гигабайтах реальных исходников, и что там будет часто встречаться (при этом не являясь ошибкой), тоже будет загнано в исключения.
size_t offset = static_cast<size_t>(int_var_1 + int_var_2);

Мну тут таки не согласно. Проблема тут не в int32/unit64, а скорее в signed / unsigned операциях. В зависимости от компилятора int может быть и 32 и 64, но size_t точно не меньше их размеров и точно unsigned. Так как int_var_1 и int_var_2 тут это количество и смещение (всегда неотрицательные), их сумма точно всегда влезет в size_t -- но не факт что всегда влезет в int.

Анализатор отталкивается не от теоретических, а практических типов :). В данном режиме сборки int 32-битный. А значит может быть переполнение.

Я к тому что неважно 32 или 64 -- переполнение возможно всё равно так как int+int не обязательно влезает в int. (К слову у меня сборка на линупсе так что всё 64бита -- и всё равно возможно! впрочем тогда надо СЛИШКОМ большие структуры... :D)

Похоже здесь static_cast<size_t> был добавлен просто для того, чтобы задушить предупреждение компилятора о преобразовании знакового int в беззнаковый size_t.

Любовь Гугла к int для индексов и количества элементов в контейнере все время наталкивается на реальность стандартной библиотеки, где для этого используется size_t.

Но больше всего меня, как пользователя protobuf, раздражает их использование int для размера буфера. Ведь в Гугле уверены, что 640 кБ 2 ГБ хватит всем. Поэтому я останавливаюсь, вздыхаю и мысленно ругаюсь, когда приходится писать или читать кода вида

const std::string buf = protobufMessage.SerialzeAsString(); // ага, авторы protobuf решили, что std::string это самый подходящий тип для буфера с бинарными данными

protobufMessage.ParseFromArray(buf.data(), static_cast<int>(buf.size())); // избавляемся от предупреждения и верим что тут всегда будет меньше 2 ГБ

Жаль, что после релиза уже нельзя исправить сигнатуры — у всех пользователей библиотеки, которые глушили предупреждения cast-ами, снова полезут warning-и.

Со сменой типа параметров это может быть чуть проще. При желании можно добавить перегруженную версию ParseFromArray(const void* data, size_t size), опционально пометить ParseFromArray(const void* data, int size) как deprecated.

При смене возвращаемого типа тоже все решаемо, например какByteSize() -> ByteSizeLong().

Проблема в точке зрения авторов protobuf, что int для размеров это нормально.

Да, не подумал. В принципе, int-размеров достаточно для всех моих применений, но неидеоматичность современному C++ и постоянные касты неприятны.

Спасибо! Подготовил исправления по всем пунктам :)

Sign up to leave a comment.