# V525. Code contains collection of similar blocks. Check items X, Y, Z, ... in lines N1, N2, N3, ...

Анализатор обнаружил код, который возможно содержит опечатку. Этот код можно разделить на более мелкие и похожие между собой фрагменты кода. Хотя фрагменты кода похожи, но все же различны. Высока вероятность, что подобный код был создан с использованием подхода Copy-Paste. Сообщение V525 будет выдано в том случае, если есть подозрение, что один из элементов не исправлен в скопированном тексте. Ошибка может находиться в одной из строк, номера которых содержатся в сообщении V525.

Недостатки сообщения V525:

1) Данное диагностическое правило основано на эвристических методах и нередко дает ложное срабатывание.

2) Реализация эвристического алгоритма сложна и занимает более 1000 строк кода на Си++. Поэтому его затруднительно описать в рамках документации. Как следствие пользователю может быть трудно понять, почему выдано то или иное сообщение V525.

3) Диагностическое сообщение относится к нескольким строкам, а не к одной. Указать только одну строку невозможно, так как ошибка может быть в любой из них.

Преимущества сообщения V525:

1) Можно обнаружить ошибки, которые крайне сложно заметить при обзоре кода (code review).

Рассмотрим вначале искусственный пример:

    ...
    float rgba[4];
    rgba[0] = object.GetR();
    rgba[1] = object.GetG();
    rgba[2] = object.GetB();
    rgba[3] = object.GetR();

Массив 'rgba' представляет собой цвет и прозрачность некоего объекта. При написании кода, заполняющего массив, вначале была написана строчка "rgba[0] = object.GetR();". Затем это строка была несколько раз скопирована и изменена. Однако в последней строке изменения сделаны не до конца и вместо функции 'GetA()' вызывается 'GetR()'. Анализатор выдает на данный код следующее предупреждение:

V525: The code containing the collection of similar blocks. Check items 'GetR', 'GetG', 'GetB', 'GetR' in lines 12, 13, 14, 15.

Просмотрев строки 12, 13, 14 и 15 можно обнаружить ошибку. Исправленный вариант кода:

    rgba[3] = object.GetA();

Теперь рассмотрим несколько примеров, взятых из кода реальных приложений. Первый пример:

    tbb[0].iBitmap = 0;
    tbb[0].idCommand = IDC_TB_EXIT;
    tbb[0].fsState = TBSTATE_ENABLED;
    tbb[0].fsStyle = BTNS_BUTTON;
    tbb[0].dwData = 0;
    tbb[0].iString = -1;
    ...
    tbb[6].iBitmap = 6;
    tbb[6].idCommand = IDC_TB_SETTINGS;
    tbb[6].fsState = TBSTATE_ENABLED;
    tbb[6].fsStyle = BTNS_BUTTON;
    tbb[6].dwData = 0;
    tbb[6].iString = -1;

    tbb[7].iBitmap = 7;
    tbb[7].idCommand = IDC_TB_CALC;
    tbb[7].fsState = TBSTATE_ENABLED;
    tbb[7].fsStyle = BTNS_BUTTON;
    tbb[6].dwData = 0;
    tbb[7].iString = -1;

Фрагмент кода приведен далеко не полностью. Вырезано более чем половина. Фрагмент писался методом копирования и правки кода. Неудивительно, что в таком большом фрагменте затерялся неверный индекс. Анализатор выдает следующее диагностическое сообщение: "The code containing the collection of similar blocks. Check items '0', '1', '2', '3', '4', '5', '6', '6' in lines 589, 596, 603, 610, 617, 624, 631, 638". Просмотрев данные строки, мы можем исправить дважды повторяющийся индекс '6'. Исправленный вариант кода:

    tbb[7].iBitmap = 7;
    tbb[7].idCommand = IDC_TB_CALC;
    tbb[7].fsState = TBSTATE_ENABLED;
    tbb[7].fsStyle = BTNS_BUTTON;
    tbb[7].dwData = 0;
    tbb[7].iString = -1;

Второй пример:

    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITTEXT,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLOSEALL, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLOSE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_SAVELAYOUT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_RESIZE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_REFRESH, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITTEXT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_SAVE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITIMAGE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLONE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

Рассматривая данный код, очень сложно найти в нем ошибку. Однако ошибка здесь действительно присутствует. Дважды модифицируется состояние одного и того же пункта меню 'ID_CONTEXT_EDITTEXT'. Выделим две повторяющиеся строки:

    ------------------------------
    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITTEXT,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    ------------------------------
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLOSEALL, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLOSE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_SAVELAYOUT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_RESIZE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_REFRESH, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    ------------------------------
    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITTEXT, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    ------------------------------
    pPopup->EnableMenuItem(
      ID_CONTEXT_SAVE, MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_EDITIMAGE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);
    pPopup->EnableMenuItem(
      ID_CONTEXT_CLONE,MF_GRAYED|MF_DISABLED|MF_BYCOMMAND);

Возможно это несерьезная погрешность, и одна из строк просто лишняя. А возможно, здесь забыли изменить состояние какого-то другого элемента меню.

К сожалению, анализатор, осуществляя данный вид диагностики, часто ошибается и генерирует ложные срабатывания. Пример кода, приводящий к ложному срабатыванию:

    switch (i) {
      case 0: f1 = 2; f2 = 3; break;
      case 1: f1 = 0; f2 = 3; break;
      case 2: f1 = 1; f2 = 3; break;
      case 3: f1 = 1; f2 = 2; break;
      case 4: f1 = 2; f2 = 0; break;
      case 5: f1 = 0; f2 = 1; break;
    }

Здесь анализатору не нравится корректный столбец цифр: 2, 0, 1, 1, 2, 0. В подобных ситуациях можно воспользоваться подавлением предупреждений, вписав комментарий //-V525 в конце строки:

    switch (i) {
      case 0: f1 = 2; f2 = 3; break; //-V525
      case 1: f1 = 0; f2 = 3; break;
      case 2: f1 = 1; f2 = 3; break;
      case 3: f1 = 1; f2 = 2; break;
      case 4: f1 = 2; f2 = 0; break;
      case 5: f1 = 0; f2 = 1; break;
    }

Если ложных предупреждений слишком много, то можно отключить данное диагностическое правило в настройках анализатора. Также мы будем благодарны, если вы напишите нам в поддержку о тех ситуациях, когда происходят ложные срабатывания, и мы постараемся улучшить алгоритм диагностики. Просьба в письме приводить соответствующие фрагменты кода.

Данная диагностика классифицируется как:

CWE-682

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V525.