Инструменты статического анализа значительно продвинулись вперед за время своего существования. Они уже не напоминают «линтеры», активно использовавшиеся 20 лет назад. Но некоторые программисты до сих пор считают их крайне примитивными инструментами. И это очень печально. Больно видеть, что методология статического анализа вообще и наш анализатор PVS-Studio в частности так трактуются.

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

if (A[0] == 0)
{
  X = Y;
  if (A[0] == 0)
    ....
}

Анализатор говорит, что второе условие всегда верно. Действительно, при внимательном рассмотрении тела функции видно, что программист намеревался проверить какой-то другой элемент.

Теперь кто-то прокомментировал это в следующих строках:

Да, в данном конкретном случае это действительно ошибка. Но в общем случае это предупреждение неверно, поскольку элемент может изменить свое значение за время между двумя одинаковыми проверками, и в этом случае повторная проверка имеет смысл.

Это грустно. Программисты, кажется, до сих пор думают, что анализаторы кода основаны на использовании регулярных выражений — они считают, что инструмент злится, просто видя два одинаковых вложенных if :(.

Разумеется, любой современный статический анализатор отслеживает изменения значений переменных. Если переменная не изменяется, выдается предупреждение. Если это так, предупреждение не выдается. Чтобы гарантировать это, анализаторы полагаются на анализ потока данных.

И именно так работает PVS-Studio. Давайте посмотрим на следующий синтетический пример:

char get();
int foo(char *p, bool arg)
{
    if (p[1] == 1)
    {
        if (arg)
            p[0] = get();
        if (p[1] == 1)          // Warning
            return 1;
    }
    if (p[2] == 2)
    {
        if (arg)
            p[2] = get();
        if (p[2] == 2)          // Ok
            return 2;
    }
    return 3;
}

Этот код состоит из двух одинаковых блоков. В одном проверяемая переменная не меняется, а в другом меняется. Поэтому анализатор выдает предупреждение только на первом блоке: V547 Выражение ‘p[1] == 1’ всегда истинно.

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

Дополнительная литература