Ошибки, которые не находит статический анализ кода, потому что он не используется

    Время от времени читатели наших статей о проверке открытых проектов обращают внимание, что статический анализатор кода PVS-Studio выявляет большой процент ошибок, которые незначительны или вообще не влияют на работу приложения. Это действительно так. Большинство важных ошибок уже поправлены благодаря ручному тестированию, отзывам пользователей и иным дорогим методам. При этом, многие из этих ошибок можно было бы найти ещё на этапе написания кода и исправить с минимальными потерями времени, репутации, денег. В этой статье будет приведено несколько примеров реальных ошибок, которые были бы сразу исправлены, если бы авторы проектов использовали статический анализ кода.


    Идея очень проста. Поищем на GitHub примеры pull requests, в комментариях к которым указано, что это исправление ошибки. И попробуем найти эти ошибки с помощью статического анализатора кода PVS-Studio. Если исправленная ошибка находится анализатором, то это означает, что её можно было бы исправить ещё на этапе написания кода. А чем раньше ошибка исправляется, тем дешевле это обходится.

    К сожалению, GitHub подкачал и не позволил сделать большую шикарную статью на эту тему. В самом GitHub тоже есть глюк (или фича), которая не позволяет искать комментарии в pull requests в проектах, написанных только на определённых языках программирования. Ну или я не «умею его готовить». Независимо от того, что я указываю искать комментарии в проектах на языке C++, C# или Java, выдаются результаты по всем языкам, включая PHP, Python, JavaScript и так далее. В результате искать подходящие случаи оказалось крайне утомительным занятием, и я ограничусь только несколькими примерами. Тем не менее, их достаточно, чтобы продемонстрировать полезность инструментов статического анализа кода при его регулярном использовании.

    Что, если ошибка была бы отловлена на ранней стадии? Ответ несложен: программистам не понадобилось бы ждать её проявления, а затем искать и исправлять дефектный код.
    Посмотрим на ошибки, которые мог бы сразу обнаружить PVS-Studio:

    Первый пример взят из проекта SatisfactoryModLoader. До исправления ошибки код выглядел так:

    // gets an API function from the mod handler
    SML_API PVOID getAPIFunction(std::string name) {
      bool found = false;
      for (Registry reg : modHandler.APIRegistry) {
        if (reg.name == name) {
          found = true;
        }
      }
      if (!found) {
        std::string msg = ...;
        MessageBoxA(NULL, 
                    msg.c_str(), 
                    "SatisfactoryModLoader Fatal Error", 
                    MB_ICONERROR);
        abort();
      }
    }

    Этот код содержал ошибку, на которую PVS-Studio сразу выдал бы предупреждение:

    V591 Non-void function should return a value. ModFunctions.cpp 44

    Приведенная выше функция не имеет return, поэтому она будет возвращать формально неопределенное значение. Программист не пользовался анализатором кода, поэтому ему пришлось самостоятельно искать ошибку. Функция после правки:

    // gets an API function from the mod handler
    SML_API PVOID getAPIFunction(std::string name) {
      bool found = false; 
      PVOID func = NULL;
      for (Registry reg : modHandler.APIRegistry) {
        if (reg.name == name) {
          func = reg.func;
          found = true;
        }
      }
      if (!found) {
        std::string msg = ...;
        MessageBoxA(NULL, 
                    msg.c_str(), 
                    "SatisfactoryModLoader Fatal Error", 
                    MB_ICONERROR);
        abort();
      }
      return func;
    }

    Любопытно, что в коммите автор указал баг как критический: "fixed critical bug where API functions were not returned".

    Во втором коммите из истории проекта mc6809 исправления были внесены в следующий код:

    void mc6809dis_direct(
      mc6809dis__t *const dis,
      mc6809__t    *const cpu,
      const char   *const op,
      const bool          b16
    )
    {
      assert(dis != NULL);
      assert(op != NULL);
    
      addr.b[MSB] = cpu->dp;
      addr.b[LSB] = (*dis->read)(dis, dis->next++);
    
      ...
    
      if (cpu != NULL)
      {
        ...
      }
    }

    Автор исправил лишь одну строку. Он заменил выражение

    addr.b[MSB] = cpu->dp;

    на выражение

    addr.b[MSB] = cpu != NULL ? cpu->dp : 0;

    В старой версии кода не производилось никакой проверки cpu на равенство нулевому указателю. Если вдруг окажется, что в качестве второго аргумента в функцию mc6809dis_direct будет передан нулевой указатель, то в теле функции произойдет его разыменование. Результат плачевен и непредсказуем.

    Разыменование нулевого указателя — один из самых частых паттернов, по поводу которых нам говорят: «Это не критическая ошибка. Ну живет она в коде и живет, а если разыменование случится — программа спокойно упадёт и всё». Странно и грустно слышать такое от C++ программистов, но жизнь есть жизнь.

    В любом случае, в данном проекте такое разыменование всё-таки превратилось в баг, о чем нам говорит заголовок коммита: "Bug fix---NULL dereference".

    Если бы разработчик проекта пользовался PVS-Studio, он бы еще два с половиной месяца назад (именно столько прошло с момента внесения ошибки) мог проверить свой код и обнаружить предупреждение:

    V595 The 'cpu' pointer was utilized before it was verified against nullptr. Check lines: 1814, 1821. mc6809dis.c 1814

    Таким образом, ошибка была бы устранена еще в момент её появления, что сэкономило бы время и, возможно, нервы разработчика :).

    Пример еще одной интересной правки был найден в проекте libmorton.

    Исправляемый код:

    template<typename morton>
    inline bool findFirstSetBitZeroIdx(const morton x, 
                                       unsigned long* firstbit_location)
    {
    #if _MSC_VER && !_WIN64
      // 32 BIT on 32 BIT
      if (sizeof(morton) <= 4) {
        return _BitScanReverse(firstbit_location, x) != 0;
      }
      // 64 BIT on 32 BIT
      else {
        *firstbit_location = 0;
        if (_BitScanReverse(firstbit_location, (x >> 32))) { // check first part
          firstbit_location += 32;
          return true;
        }
        return _BitScanReverse(firstbit_location, (x & 0xFFFFFFFF)) != 0;
      }
    #elif  _MSC_VER && _WIN64
      ....
    #elif __GNUC__
      ....
    #endif
    }

    В своей правке программист заменяет выражение "firstbit_location += 32" на "*firstbit_location += 32". Программист ожидал, что число 32 будет прибавляться к значению переменной, к которой привязан указатель firstbit_location, но оно прибавлялось непосредственно к указателю. Измененное значение указателя больше нигде не использовалось, а ожидаемое значение переменной так и оставалось неизмененным.

    PVS-Studio выдал бы на этот код предупреждение:

    V1001 The 'firstbit_location' variable is assigned but is not used by the end of the function. morton_common.h 22

    Казалось бы, что может быть ужасного в измененном, но далее не использованном выражении? Диагностика V1001 явно не выглядит так, будто предназначена для выявления особо опасных багов. Несмотря на это, она обнаружила важную ошибку, которая влияла на логику программы.

    Более того, оказалось, что эту ошибку было не так просто найти! Мало того, что она находилась в программе с самого момента создания файла, так она еще и пережила множество правок в соседних строках и просуществовала в проекте аж целых 3(!) года! Все это время логика программы была нарушена, и она работала не совсем так, как ожидали этого разработчики. Если бы они использовали PVS-Studio, то ошибка была бы обнаружена гораздо раньше.

    Под конец рассмотрим ещё один красивый пример. Пока я собирал исправления ошибок на GitHub, я несколько раз наткнулся на фикс с вот таким содержанием. Исправленная ошибка находилась здесь:

    int kvm_arch_prepare_memory_region(...)
    {
      ...
      do {
        struct vm_area_struct *vma = find_vma(current->mm, hva);
        hva_t vm_start, vm_end;
        ...
        if (vma->vm_flags & VM_PFNMAP) {
          ...
          phys_addr_t pa = (vma->vm_pgoff << PAGE_SHIFT) +
            vm_start - vma->vm_start;
          ...
        }
        ...
      } while (hva < reg_end);
      ...
    }

    На этот участок кода PVS-Studio выдал предупреждение:

    V629 Consider inspecting the 'vma->vm_pgoff << 12' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. mmu.c 1795

    Я посмотрел объявления переменных, используемых в выражении "phys_addr_t pa = (vma->vm_pgoff << PAGE_SHIFT) + vm_start — vma->vm_start;", и обнаружил, что приведенный выше код аналогичен следующему синтетическому примеру:

    void foo(unsigned long a, unsigned long b)
    {
      unsigned long long x = (a << 12) + b;
    }

    Если значение 32-битной переменной a больше, чем 0xFFFFF, то 12 старших битов будут иметь хотя бы одно ненулевое значение. После применения к этой переменной операции сдвига влево эти значимые биты будут потеряны, вследствие чего в x будет записана некорректная информация.

    Чтобы устранить потерю старших битов, необходимо сначала привести a к типу unsigned long long, и только после этого производить операцию сдвига:

    pa = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
    pa += vm_start - vma->vm_start;

    Тогда в pa всегда будет записываться корректное значение.

    Все бы ничего, но этот баг, как и первый пример из статьи, тоже оказался критическим, о чем автор коммита написал отдельно в своем комментарии. Более того, эта ошибка попала ну просто в огромное количество проектов. Чтобы в полной мере оценить масштаб трагедии, предлагаю посмотреть на количество результатов при поиске этого багфикса на GitHub. Страшно, не правда ли?



    Итак, я применил новый подход, чтобы продемонстрировать пользу регулярного использования статического анализатора кода. Надеюсь, вам понравилось. Скачайте и попробуйте статический анализатор кода PVS-Studio для проверки собственных проектов. На момент написания статьи в нём реализовано около 700 диагностических правил для обнаружения разнообразнейших паттернов ошибок. Поддерживаются языки C, C++, C# и Java.



    Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: George Gribkov. Errors that static code analysis does not find because it is not used
    PVS-Studio
    Статический анализ кода для C, C++, C# и Java

    Комментарии 47

      –3

      А сколько всего было просмотрено пулл реквестов? Если каждый просмотренный содержал ошибки, которые находил анализатор, то это хорошо. Если же пришлось просматривать множество фиксов, то совершенно другое. Сложно говорить о полезности инструмента для фикса критичных багов без такой информации.

        +3
        Много. Но прежде чем говорить «фи, тогда не интересно», попробуйте сами поискать :). Крайне неблагодарное занятие. Во-первых, при поиске по комментарию, не работает фильтр по языкам и лезут всякие правки в html и т.д. А во-вторых, много багов лежит не в плоскости анализа кода (не тот цвет кнопки, поправили логику работы меню, использовали не ту формулу и т.д.).
          +2
          PVS — штука определённо полезная, как и данная статья.
          Но для совсем честного понимания выгоды от использования PVS случаи
          не тот цвет кнопки, поправили логику работы меню, использовали не ту формулу
          тоже в общем-то надо учитывать.
            +1
            Совершенно верно. Мы всегда говорим, что если хочется получить качественное ПО, следует использовать одновременно несколько методологий (придерживаться принятого стандарта кодирования, юнит-тесты, ручное тестирование, регрессионные тесты, динамический анализ, статический анализ и т.д.)
              0
              Спасибо, за очередное интересное исследование. А можно как то получить временно на месяц, сравнить с PC Lint. PC Lint, я так понял полноценный компилятор почти… т. е он проверяет исходный код и не нуждается в наличии стороннего компилятора. Это будет имееть какое то преимущество перед PVS-Studio? Просто, напрмер, я сравнивал PC Lint и IAR CSTAT. Ну это небо и земля. CSTAT половину ошибок дает, на, то чего в коде даже нет, но видимо есть в обьектных файлах есть после компилятора.
              Плюсом, Lint делает еще замечания, типа бест практис, что так лучше не делать, но ничего страшного…
                0
                А можно как то получить временно на месяц, сравнить с PC Lint.
                Можно. Напишите нам и мы выдадим Вам ключ на месяц.
                PC Lint, я так понял полноценный компилятор почти… т. е он проверяет исходный код и не нуждается в наличии стороннего компилятора.
                Так это… PVS-Studio тоже почти полноценный компилятор (по разбору кода). Внешние компиляторы используются только для препроцессирования. Препроцессировали внешним компилятором, сами построили дерево разбора и давай применять разные технологии анализа :).
                P.S. Почему мы сами не пишем о сравнении PVS-Studio с другими статическими анализаторами кода: www.viva64.com/ru/b/0637
        0

        Всегда с интересом читаю ваши статьи.
        Но в данном случае мне непонятно одно… Почему стандарт языка, а за ним все следом говорят о том, что разыменование указателя с адресом 0 является UB? Ноль — это всего лишь адрес памяти к которому так же можно "достучаться" (я даже доказал это на одном известном форуме).
        С таким же успехом можно сказать про любой указатель в любой программе.
        Дело в том, что разработчики стандарта языка просто приняли за должное считать nullptr невалидным адресом. Но почему-то они не учли, что на языке C++ можно писать без конкретной ОС. Вообще без ОС, где по нулевому адресу (например на архитектуре x86) находится таблица векторов прерываний.
        Хотелось бы услышать ваше мнение по этому поводу.

          +2
          По факту в большинстве взрослых архитектур и ос (16 битный дос не берем) первые 64к адресного пространства процессов огорожены или вообще не выделены, т.е. любое обращение к ним даст segfault.
          Глубинный смысл именно в защите памяти от ошибок работы с указателями. Ну как в этой статье зевнули *. Лучше упасть так, чем запороть данные и дальше неизбежно пойти в разнос.
            –1
            По факту в большинстве взрослых архитектур и ос (16 битный дос не берем) первые 64к адресного пространства процессов огорожены или вообще не выделены, т.е. любое обращение к ним даст segfault.
            По какому-такому факту? Кто вам сказал этот бред? Что значит «не выделены»? Кто их должен «выделять»?
            Я легко могу обратиться к адресу ниже 64к на x86 без ОС.
            И да, разделяйте понятия «архитектура» и «ОС».
            Кстати, процессор архитектуры x86 изначально запускается в 16-битном режиме, а уж после (по требованию загрузчика или ОС) переходит в защещенный.
              +2
              Разыменование нулевого указателя не является undefined behavior. Лишь доступ к этому значению. Самый тупой пример &*(p = (void*)0) вполне определено. Кстати, обратите внимание на void. Само по себе разыменовывание 0 теоретически означает разыменовывание 0 адреса, что опять-таки без проблем, хотя тут тоже стандарт как-то не очень об этом говорит. А и segfault — это ядро ругается, и это не часть стадарта. Поверьте, если бы любое разыменование нулевого указателя приводило к Segfault, то и уязвимостей бы не было. А тут, сами посмотрите github.com/FFmpeg/FFmpeg/commits/master от 12 июля Null dereference, сюрприз!

              Кстати, тут недавно все спорили насчет &p->element. Дак вот, я считаю, что стандарт об это ничего не говорит вообще. Это такое крупное пятно.
              Но давайте всё-таки определим несколько вещей. Вы понимаете, что по факту нет никакого оператора ->? Это просто shorthand для (*p).element (скобки обязательны, поэтому и shorthand). Очевидно, что это туфта какая-то для NULL. Теперь добавим &. Оно раскрывается в &((*p).element). Обратите внимание на еще одни скобки. Понимаете, вы берете & от element! Но структуры то нет… В общем это как-то не очень само по себе. На самом деле все еще сложнее…

              Теперь про offsetof. Wikipedia уже исправили.
              en.wikipedia.org/wiki/Offsetof
              «It has generated some debate if this is undefined behavior according to the C standard, since it appears to involve a dereference of a null pointer (although, according to the standard, section 6.6 Constant Expressions, Paragraph 9, the value of the object is not accessed by the operation).

              И кстати, когда вы искали на stackechange, слона то вы и не заметили, там такое в одном месте есть…
              stackoverflow.com/questions/28482809/c-access-static-members-using-null-pointer/28483477
                0
                Дак вот, я считаю, что стандарт об это ничего не говорит вообще.

                Разве в C99 нет специального параграфа именно по поводу суперпозиции & и *?


                C99, §6.5.3.2
                The unary & operator yields the address of its operand.

                If the operand is the result of a unary * operator,
                neither that operator nor the & operator is evaluated and the result is as if both were
                omitted, except that the constraints on the operators still apply and the result is not an
                lvalue.

                Даже больше, есть специальная сноска, явно определяющая &*NULL как корректную операцию:


                C99, footnote 87
                Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)).
                  +1
                  Дак вот, я считаю, что стандарт об это ничего не говорит вообще.
                  Разве в C99 нет специального параграфа именно по поводу суперпозиции & и *?

                  Так тут, насколько я понимаю, явной суперпозиции нет (в случае &(p->element)). Порядок операций-то такой — dereference, обращение к полю, получение адреса, т.е. & и * идут не подряд, и "свернуть" их так, как указано в цитате, нельзя.

                  0
                  Разыменование нулевого указателя не является undefined behavior.
                  Согласно стандарту — является. В последнем черновике не удалось беглым взглядом найти это утверждение. Приведу другой источник.

                  Само по себе разыменовывание 0 теоретически означает разыменовывание 0 адреса...
                  Так я об этом и писал.

                  А и segfault — это ядро ругается, и это не часть стадарта.
                  Про segfault я ничего и не говорил. Это к товарищу vanxant.
                  0
                  Месье каждый день пишет или хотя бы запускает софт, работающий в реальном режиме х86? Кроме самых первых стадий инициализации биоса мне даже в голову ничего не приходит.
                  В ембеде да, такое встречается. Но там у них вообще, главный кукбук — еррата на конкретный чип.
                    +1
                    Может пишет, а может и не пишет. Зависит от настроения. Вы вообще к чему это спросили? Какое это имеет отношение к данной теме?

                    Вы можете ответить хотя бы на один из моих вопросов?
                      0
                      Спросил я это к тому, что в реальной жизни, даже если как-то обхитрить компилятор, за обращение по адресу ноль подавляющее число ОС грохнут ваш процесс. Поэтому даже не очень важно, что там написано в стандарте языка.
                      Отвечать на поток вопросов в стиле «кто вам сказал этот бред» я не намерен.
                        0
                        Интересный вы человек…
                        Отвечать на поток вопросов в стиле «кто вам сказал этот бред» я не намерен.
                        Ну ответьте хотя-бы на не бредовые вопросы.
                        Я Вас чем-то обидел?
                          0
                          Задайте вопрос по теме — постараюсь ответить. Я пока просто не вижу, о чём вы хотите узнать.
                          Почему разыменование nullptr это UB? Я уже ответил: потому что в большинстве ОС это может привести к краху процесса. Но в каких-то случаях (например, системах без MMU) — может и не привести. При этом в начале адресного пространства процесса обычно ОСи хранят всякие системные таблицы — про память, права, дескрипторы и т.д, лазить в которые как минимум опасно и может привести к непредсказуемым эффектам. Так что это именно undefined, а не unspecified behaviour.
                  0

                  Это не "наезд", не обижайтесь.

                  +2
                  Всё это уже обсуждалось: 1, 2. С практической точки зрения, лично для нас, тема не очень интересна. Для тех систем, где применяется PVS-Studio, разыменование нулевого указателя — это беда. Если PVS-Studio столкнётся с системой, где надо писать по нулевому указателю, это будет особый случай и всё равно там будет куча подпорок для компиляторов и анализаторов, чтобы они «замолчали» на разный странный код :).
                    –1

                    Вот только нулевой указатель (nullptr) и указатель на нулевой адрес памяти (reinterpret_cast<T*>((int)0)) — это разные значения, которые могут "случайно" совпасть в рантайме.

                      –2
                      Мы сейчас про С++ говорим? nullptr это вообще то не указатель. Он может вернуть указатель, но указателем не является. Поэтому я не могу понять ваше второе предложение… Вы теплое пытаетесь сравнить с мягким
                      +1

                      AntonSazonov


                      Почему стандарт языка, а за ним все следом говорят о том, что разыменование указателя с адресом 0 является UB?

                      Прежде всего, неопределенным поведением является разыменование именно некорректного указателя, а не указателя с адресом 0:


                      C99, footnote 87
                      Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.

                      При этом под нулевым указателем понимается не указатель с адресом 0, а некоторая константа NULL, или nullptr с внутренним представлением, которое вполне может оказаться не 0. Другими словами, для корректного указателя ptr верно ptr != NULL, даже если (uintptr_t) ptr == 0.


                      C99, §6.3.2.3
                      An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

                      P.S. Занимательно, что в таком случае указатели ptr1 и ptr2


                      uintptr_t zero = 0;
                      void
                        *ptr1 = (void *) 0,  /* == NULL */
                        *ptr2 = (void *) zero;  /* != NULL */

                      могут оказаться не равны, потому что zero для ptr2 не является integer constant expression.

                        0

                        Именно, что на разных архитектурах будет разное поведение.
                        И поэтому в общем оно не определено. Когда вы программируете embedded-систему, где это легальный адрес, поведение будет тоже вполне определённым. Но тут речь о стандарте всего языка, а не только в применении к микроконтроллерам. Именно поэтому оно и называется прямо, как есть — undefined.


                        (к слову, не так досаждает попытка доступа по нулевому адресу, как аналогичная по околонулевому. Когда указатель на инстанс класса нулевой, но при этом конкретное поле уже имеет другое (ненулевое) значение. Например, 0x10. Проверку на "не-нуль" оно проходит, но при использовании это всё равно UB).

                          +2

                          То, что вы предлагаете называть undefined, в стандарте называется unspecified. Код, содержащий undefined behaviour, не может быть корректным в частном случае, в отличие от кода, содержащего unspecified behaviour. Вы согласны с тем, что следующий код не содержит UB?


                          void foo(uintptr_t offset) {
                            char *ptr = (char *) offset;
                            char val = *ptr;
                          }
                          
                          foo(0);

                          От компилятора зависит, как именно offset будет приведен к ptr.
                          От архитектуры и операционной системы зависит, что именно случится при чтении по адресу ptr.
                          На мой взгляд, здесь нет неопределенного поведения с точки зрения языковой грамматики.

                        –1
                        Если PVS-Studio столкнётся с системой, где надо писать по нулевому указателю...
                        А есть такая вероятность?)
                          –1
                          Ждём статью о том, какие ошибки были допущены при написании PVS-Studio и были выявлены и исправлены после проверки инструментом PVS-Studio.
                            +1
                              +1
                              Для тех, кто в теме.


                              Мы регулярно проверяем наш статический анализатор самим собой.

                              Во-первых, у нас используется инкрементальный анализ. То есть анализатор автоматически стартует на файлах, которые только что были перекомпилированы. И ошибки (которые обнаруживаются анализатором) если и появляются в нашем коде, то сразу же исправляются. При этом они не попадают ни в систему контроля версий, ни в баг-трекер.

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

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

                              НО! Кое-что есть: Проверяем исходный код плагина PVS-Studio с помощью PVS-Studio.
                                0
                                > В итоге, хотя мы и регулярно проверяем свой код, мы никогда не сможем написать статью об ошибках, найденных с помощью нашего анализатора в нем самом.

                                Вот этого вывода я не понял.
                                Если, например, новой эвристикой вы находите что-то, что раньше не находилось, почему бы не написать об этом на примере собственного кода?
                                Да, сбор данных будет не одномоментный, а растянутый во времени, но всё равно может набраться достаточно интересных примеров.
                                  +2
                                  Очень сложно подвигнуть коллег программистов всё это аккуратно выписывать в какое-то общее место. Поправили и пошли дальше программировать. По себе знаю. Неоднократно было. Поправляешь ошибку, и только потом уже вспоминаешь, что хорошо бы записать. Но кода то с ошибкой уже нет, надо править обратно… В общем лень и тихо мирно делаешь вид, что ничего и не было :).
                              0
                              Глупый вопрос по первому примеру: а разве сам компилятор не выдавал предупреждения при компиляции, что функция не возвращает результат?
                              Мне казалось, что GCC вполне себе выкидывал предупреждения (хотя, давно не писал на С++, может и ошибаюсь) и исправить можно было и без анализатора. Тогда тут разработчики сами виноваты, что предупреждения не читают (еще, как вариант: у них столько предупреждений при компиляции, что они на них уже не обращают внимания).
                              0
                              вы бы знали как этого не хватает в Питоне))
                              язык замечательный, но лажа вокруг него…
                              начиная от помощника IDE который невероятно хуже чем в Java

                              у вас идей нет сделать кросс -языковой вариант (просто по набору правил, тогда расширить можно для всего) или вариант чтобы юзем мог програмно его вызывать в качестве макро движка было бы круто
                                0
                                > у вас идей нет сделать кросс -языковой вариант
                                Нет. Если уж делать, то полноценный анализатор.
                                  0
                                  просто сделайте расширяемым чтобы люди могли сами кидать свои правила общение юзеров вроде форума и возможностью сохранять и загружать набор правил на гитхаб
                                    +5
                                    Это всё фантазии. Разработка статического анализа — это сложная задача, требующая погружения в предметную область. Поэтому, теоретически, да все будут писать правила. А как дело доходит до практики, то Cppcheck объявляет сбор средств, чтобы нормально научиться искать неинициализированные переменные.
                                      –2
                                      вы собираете данные из реальных проэктов поповоду того какие там баги?
                                        +1
                                          –1
                                          например пользователь устанавливает это у себя в IDE и потом по сообщению IDE об ошибке используется эвристика для поиска (по эксепшну) или при нахождении неизвестного бага он заносится в список и потом его можно сохранить у себя или посылается вам в базу.
                                            +5
                                            Нет, мы не воруем исходники у пользователей.

                                            А то, что вы пишите именно так и будет восприниматься.
                                              –3
                                              ну вам же нужно как то базу расширять. Сообщение может посылаться максимально оторванным от кода — только паттерн бага с переменными названными по другому, чтобы ничего воровать. Кстати я тут просмотрел базу, вы некоторые правильные выражения записали как баги, ну что неправильного в:

                                              Consider inspecting the statement of '*pointer++' pattern. Probably meant: '(*pointer)++'.

                                              обычный инкремент поинтера

                                              The 'first' argument of 'Foo' function is equal to the 'second' argument.

                                              тоже часто бывает в векторных операциях и т.п, ничего криминального

                                              A suspicious expression 'A[B < C]'. Probably meant 'A[B] < C'.

                                              А это с какой стати вообще?

                                                +3
                                                обычный инкремент поинтера

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


                                                тоже часто бывает в векторных операциях и т.п, ничего криминального

                                                Читайте внимательнее:


                                                Передача одного и того же значения в качестве двух аргументов для многих функций является нормальной ситуацией. Но если речь идет о таких функциях как memmove, memcpy, strstr, strncmp, то это очень подозрительная ситуация.

                                                А это с какой стати вообще?

                                                А Вы умеете индексировать массивы boolean-ами, да ещё и так, что код получается понятным, читабельным и поддерживаемым?

                                      +6
                                      просто

                                      просто???

                                      как_нарисовать_сову.jpg

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

                                Самое читаемое