Pull to refresh

Comments 33

REENTERABLE – повторный вызов с теми же аргументами даст тот же результат;

Ай-ай-ай. Реентерабельность означает, что функцию допустимо вызывать в то время когда она уже выполняется. Это свойство функций становится важным при обработке прерываний в ядре или сигналов в юзермоде. Или при написании рекурсивных алгоритмов с обратными вызовами.


А то что вы написали, называется идемпотентностью или более сильно — чистотой (pure).

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

Массовые переименования не так уж и сложны, было бы желание. Условно говоря, что-то вроде такого однострочника:


find src -name \*.cpp -or -name \*.h | while read f; do
  sed -i 's/REENTERABLE/IDEMPOTENT/g' $f
done

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

Так purity тоже не про это, а в первую очередь про то, что нет side-эффектов, что является достаточным, но не необходимым условием возврата одинакового результата при вызовах с одинаковыми наборами аргументов.

Потому я и написал, что чистота — более сильное свойство.

Кажется, вам указали на то, что чистоту и идемпотентность нельзя "вложить" друг в друга, то есть чистота — не более сильное свойство.

Почему нельзя-то? Любая чистая функция — идемпотентна. Если считаете иначе, приведите контрпример.

Спасибо за продукт и статью! К сожалению, сейчас мало пишу на С++, но но пару раз использовал trial PVS-Studio, было очень полезно, особенно при переносе с 32 на 64bit.
Еще в постах очень рисунки стильные. Кто автор, если не секрет?
Что касается symbolic execution. У вас свой engine или сторонний? Какие ограничения? Как с path explosion боретесь?
Автор картинок найден на фрилансе, но часто мы сами что-то комбинируем, правим. Автор не стал выкладывать единорогов в резюме (возможно стесняется :), так что я, пожалуй, не буду давать ссылку.

По поводу symbolic execution. У нас всё своё. Ограничений и недоработок много, но мы постоянно улучшаем эти механизмы. С path explosion боремся, представляя значения переменных как диапазон значений/множество значений. Т.е. в каждой точке мы знаем, какой диапазон значений может иметь та или иная переменная. На самом деле всё сложнее. И да, у такого подхода есть свои проблемы. Зато быстро, так как не надо вновь и вновь анализировать одни и те-же участки кода с разным набором значений переменных. Хотя это не помогает, например, при раскрытии шаблонов и бывает надо заново анализировать класс при инстанцировании его другой константой.
Добавьте в ЧАВО :)
У меня почему-то тоже при чтении именно этой статьи (из многих ваших прочитанных) возник именно этот вопрос
В примере со строками вы говорите, что
Программист хотел проверить, что все символы в строке являются заглавными. Код явно содержит какую-то логическую ошибку, так как ранее все символы этой строки были преобразованы в нижний регистр.

А что если программист хотел именно проверить, что строка не изменяется при изменении регистра, например, состоит из небуквенных символов и цифр (на это намекает название переменной insensitiveOverride )? Тогда код вполне корректный.
Быть может, но это у него тоже не получится. Посмотрите код внимательнее. Одна строка всегда в нижнем регистре. Другая всегда в верхнем. Сравнивать их не имеет смысла.
Так ведь о том и речь. Например, так:
string value = "*123*";
string lowerValue = value.ToLower(); // lowerValue == "*123*"
string upperValue = lowerValue.ToUpper(); // upperValue == "*123*"
bool insensitive = lowerValue == upperValue; // true

и так:
string value = "aBc123";
string lowerValue = value.ToLower(); // lowerValue == "abc123"
string upperValue = lowerValue.ToUpper(); // upperValue == "ABC123"
bool insensitive = lowerValue == upperValue; // false

Если, конечно, я правильно понимаю, как работают функции такого типа.
Согласен. Поспешил. Тогда будем просто считать, что это код, который стоит лишний раз проверить. :) Надо было выбрать какой-то другой пример.

Ну почему же. Для строки "0123456789.,;!@#$%^&*()" условие будет выполняться.

Спасибо за статью!
Кстати в ней упоминается FCEUX а была когда нибудь статья по его проверке, я что то не нашёл.
Не было. Быть может интересных ошибок мало или сам проект маленький, не помню уже.
Не использовать clang для разбора C++ — вот тупиковый путь
На самом деле это очень спорное утверждение. Переход на Clang повлечёт огромную работу объёмом в многие человеко-годы. Т.е. только через 2-3 года мы будем иметь тоже самое, что и сейчас (причём в лучшем случае). У нас нет этих лишних лет. Лучше за это время мы сделаем что-то полезное и новое. Причем, не известно, что в результате будет легче поддерживать: свой движок или накатывать тысячи патчей на каждую новую версию Clang. Про тысячи патчей я не придумываю. Я знаю из статей и личного общения, что этим обречены заниматься авторы анализаторов кода, которые решили строить своё решение на основе сторонних движков. Причин появления всех этих подпорок несколько. Например, надо поддерживать экзотические расширения, существующие в других компиляторов. В данном случае, ими будут Visual C++ и GCC. Правки нужны и для того, чтобы добраться до определённой информации или узнать что-то хитрое, о чем не думали разработчики Clang. Причем все эти патчи будет невозможно пропихнуть в основную ветку, так как никому кроме разработчиков анализатора они не нужны. А поддерживать в актуальном состоянии тысячи патчей — та ещё работёнка.
Я знаю из статей и личного общения, что этим обречены заниматься авторы анализаторов кода, которые решили строить своё решение на основе сторонних движков.

Можете рассказать о таких решениях? Я не слышал.
clang успешно поддерживает расширения microsoft. О поддержке gcc я вообще молчу.
И не надо пугать сообщениями про тысячи патчей.
На что конкретно вы завязываетесь при использовании clang?
На дерево разбора, не так ли?
Вместо того, чтобы работать непосредственно над анализом кода, будете вручную писать поддержку идущего в народ C++17, которая займет не меньше времени.
Почему тогда заиспользовали Roslyn для C#?
Да потому, что сэкономили кучу времени, запилив за месяц свой анализатор на готовом движке.
Причем все эти патчи будет невозможно пропихнуть в основную ветку, так как никому кроме разработчиков анализатора они не нужны.

Пробовали?
Можете рассказать о таких решениях? Я не слышал.
Например, в статье Coverity от 2010 года говорится, что они вставляют #ifdef COVERITY в 406 мест. Думаю, со временем всё становится только хуже. Я слышал от разработчиках других анализаторов о тысячах вставок. Пруфов не будет, так как обычно про такие вещи разработчики в статьях не пишут.

clang успешно поддерживает расширения microsoft. О поддержке gcc я вообще молчу.
Ха! Это теоретически. А на практике даже работа препроцессоров бывает не совместима, и мы с этим мучаемся. Именно поэтому плагин PVS-Studio для Visual Studio, в начале мы пытаемся препроцессировать файлы с помощью Clang, а если не получается, то запускается cl (Visual C++). Про нестандартные расширения, которые даже бывают толком нигде не описаны, я вообще молчу. А поддерживать их надо, так как они могут использоваться в системных заголовочных файлах.

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

По поводу на что завязываемся… Да просто надо всё заново переписать. А потом вечно патчить. И поверьте, писать поддержку новых конструкций языка не так сложно по сравнению с объемом прочих работ. Собственно, мы уже много лет это успешно делаем.

Почему тогда заиспользовали Roslyn для C#?
Потому, что это дало возможность быстро сделать анализатор. И за это решение мы уже платим большим количеством сложностей и неудобств, связанных с постоянным изменением самого Roslyn. Например, JB не стали брать Roslyn, осознавая какую головную боль он им даст и продолжают развивать собственный движок.

Пробовали?
Пробовали. Байтораздерающий процесс.
Можете написать или посоветовать статью про нюансы препроцессора в gcc/clang/VS? В стандарте препроцессор описан очень кратко, значит, простор для реализации большой. На форумах я видел жалобы, что программа собранная в gcc (без использования специфичных расширений) при сборке clang не проходит тесты, но там было совсем немного и без разбора.
Я не встречал статью, где бы рассматривался этот вопрос. Сам тоже не писал, так как не вижу практической пользы.
clang не может корректно переварить исходники для gcc. Я имею в виду встроенное построение дерева.
Часть расширений он просто игнорирует, на пример некоторые атрибуты.
Я думаю, что автор под «поддержать нестандартные расширения» подразумевал именно — «надо сделать так, чтобы парсер не падал при встрече таких лексем в коде».
Clang, даже если что-то не поддерживает, не падает и дает проанализировать дерево, которые смог построить. Для PVS-Studio кажется будет вполне достаточно
PVS-Studio не использует непосредственно математический аппарат грамматик для поиска ошибок. Анализатор работает на более высоком уровне. Анализ выполняется на основании дерева разбора.

Не совсем понятно как с помощью грамматик искать ошибки. Я думал, что они используются для генерации парсеров, которые как раз и строят дерево разбора.

Спасибо за статью,
было бы интересно узнать сравнение с Facebook Infer, и в целом отношение к separation logic, на которой в последнее время строят статические анализаторы.
На Facebook Infer мы практически не смотрели. На первый взгляд, это достаточно ограниченный анализатор, заточенный для поиска небольшого количества паттернов ошибок. У нас же около 300 диагностик, которые подходят для C. Сравниваться с ним мы можем только на языке Си, что не очень интересно.

По поводу «separation logic, на которой в последнее время строят статические анализаторы». Это последнее время, наверное лет 20 :). Сейчас уже никто не пишет анализаторы, на таких технологиях, как регулярные выражения и т.п. Вернее некоторые пишут, но говорить о их серьёзности смысла нет. Сейчас любой статический анализатор в том или ином виде пытается учитывать значения переменных, путаются понять на что указывает указатель и т.д. В любом случае я не силён в умных словах, так как мне не требуется писать статьи в научные журналы. :)
Sign up to leave a comment.