clang-tidy: Как подавить предупреждения?

Недавно я начал экспериментировать с clang-tidy инструментом llvm. Теперь я пытаюсь подавить ложные предупреждения от кода сторонней библиотеки. Для этого я хочу использовать параметры командной строки

-header-filter=<string> or -line-filter=<string>

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

Вопрос

Какой параметр нужно дать инструменту clang-tidy, чтобы подавить предупреждение из определенной строки и файла?

если это невозможно

Какой параметр работает для подавления предупреждений из внешних файлов заголовков?


Что я сделал до сих пор

Мой первоначальный призыв к clang-tidy выглядит так

clang-tidy-3.8 -checks=-*,clang-analyzer-*,-clang-analyzer-alpha* -p Generated/LinuxMakeClangNoPCH Sources/CodeAssistant/ModuleListsFileManipulator_fixtures.cpp

и первая строка выданного предупреждения, которое я хочу подавить, выглядит так

.../gmock/gmock-spec-builders.h:1272:5: warning: Use of memory after it is freed [clang-analyzer-cplusplus.NewDelete]
    return function_mocker_->AddNewExpectation(

Люди из gmock сказали мне, что это ложное срабатывание, поэтому я хочу его подавить. Сначала попробовал использовать опцию -line-filter=<string>. В документации говорится:

  -line-filter=<string>      - List of files with line ranges to filter the
                               warnings. Can be used together with
                               -header-filter. The format of the list is a JSON
                               array of objects:
                                 [
                                   {"name":"file1.cpp","lines":[[1,3],[5,7]]},
                                   {"name":"file2.h"}
                                 ]

Я предположил, что предупреждения в данных строках отфильтрованы. Но doc doesent говорит, отфильтрованы они или внутри. После некоторой возни я создал файл .json с содержимым

[
  {"name":"gmock-spec-builders.h","lines":[[1272,1272]]}
]

и изменил командную строку на

clang-tidy-3.8 -checks=-*,clang-analyzer-*,-clang-analyzer-alpha* -p Generated/LinuxMakeClangNoPCH -line-filter="$(< Sources/CodeAssistant/CodeAssistant_ClangTidySuppressions.json)" Sources/CodeAssistant/ModuleListsFileManipulator_fixtures.cpp

который записывает содержимое файла в аргумент. Это подавляет предупреждение, но не только это предупреждение, но и все предупреждения из файла ModuleListsFileManipulator_fixtures.cpp. Я пробовал еще кое-что, но у меня ничего не получалось.

Я попробовал вариант -header-filter=<string>. Здесь в документации указано, что нужно указать регулярное выражение, которое соответствует всем файлам заголовков, из которых должна отображаться диагностика. Хорошо, подумал я, давайте использовать регулярное выражение, которое соответствует всему, что находится в той же папке, что и анализируемый файл .cpp. Я могу смириться с этим, хотя это может удалить предупреждения, возникающие из-за неправильного использования внешних заголовков.

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

-header-filter=.*\/CodeAssistant\/.*.h

который соответствует всем абсолютным именам файлов заголовков в папке CodeAssistant, но не подавляет предупреждения из файла gmock-spec-builders.h.

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

Спасибо за уделенное время.


person Knitschi    schedule 16.09.2016    source источник


Ответы (4)


Я решил проблему, добавив // NOLINT в строку 1790 файла gmock-spec-builders.h

Вот разница:

--- gmock-spec-builders.orig.h  2016-09-17 09:46:48.527313088 +0200
+++ gmock-spec-builders.h       2016-09-17 09:46:58.958353697 +0200
@@ -1787,7 +1787,7 @@
 #define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call)

 #define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
-    ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
+    ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call) // NOLINT
 #define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)

 #endif  // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_

Было бы неплохо либо апстримить этот патч (я вижу другие NOLINT в коде), либо опубликовать отчет об ошибке с любопытными людьми.

person David Hallas    schedule 17.09.2016
comment
Вот обсуждение, которое я провел на github с ребятами из googletest. Они могли бы упомянуть, что уже подавляют предупреждения. Мне было лень зарегистрироваться в списке рассылки llvm, чтобы публиковать ложные срабатывания. Какие инструменты понимают комментарий // NOLINT? - person Knitschi; 17.09.2016
comment
// NOLINT подбирается инструментом clang. Читая обсуждение, я думаю, что на самом деле это не ложное срабатывание, это больше похоже на то, что никогда не может произойти. Поэтому я думаю, что лучший подход - это решение NOLINT. - person David Hallas; 17.09.2016
comment
Как ни странно, ваше исправление не во всех случаях решает проблему для меня. Я также должен поставить // NOLINT за чертой 1272 return function_mocker_->AddNewExpectation( // NOLINT - person Knitschi; 05.10.2016
comment
Я думаю, что в любом случае предложу изменить googletest, потому что я не могу подавить предупреждения с помощью параметров командной строки. Но что делать, если зависимость изменить нельзя. Затем вы должны нанести // NOLINT повсюду, что несколько некрасиво. - person Knitschi; 05.10.2016

Я нашел другой неинвазивный (без добавления // NOLINT в стороннюю библиотеку) способ подавления предупреждений. Например, текущая версия Google Test не проходит некоторые cppcoreguidelines-* проверки. Следующий код позволяет вам проверить текущую разницу, исключая строки, содержащие макросы gtest:

git diff -U3 | sed '
    s/^+\( *TEST(\)/ \1/;
    s/^+\( *EXPECT_[A-Z]*(\)/ \1/;
    s/^+\( *ASSERT_[A-Z]*(\)/ \1/;
' | recountdiff | interdiff -U0 /dev/null /dev/stdin | clang-tidy-diff.py -p1 -path build

Предполагается, что файл build/compile_commands.json был сгенерирован раньше и clang-tidy-diff.py доступен в вашей среде. recountdiff и interdiff из patchutils - стандартные инструменты для работы с исправлениями.

Скрипт работает следующим образом:

  1. git diff -U3 генерирует патч с 3 строками контекста.
  2. sed ... удаляет префикс + из нежелательных строк, т.е. преобразует их в контекст.
  3. recountdiff правильные смещения (в первых диапазонах) в заголовках блоков.
  4. interdiff -U0 /dev/null /dev/stdin просто удаляет все контекстные строки из патча. В результате происходит разбиение начальных фрагментов.
  5. clang-tidy-diff.py считывает только вторые диапазоны из заголовков блоков и передает их clang-tidy через параметр -line-filter.

UPD: важно предоставить interdiff достаточное количество контекстных строк, иначе это может привести к некоторым артефактам в результате. См. Цитату из man interdiff:

Для достижения наилучших результатов в различиях должно быть не менее трех строк контекста.

В частности, я обнаружил, что git diff -U0 | ... | interdiff генерирует некоторые ложные литералы $!otj после разделения фрагментов.

person olegrog    schedule 27.02.2019

Используйте -isystem вместо -I, чтобы задать пути включения вашей системы и сторонних разработчиков. -I следует использовать только для включения кода, который является частью создаваемого проекта.

Это единственное, что требуется для clang-tidy игнорирования всех ошибок во внешнем коде. Все остальные ответы (на момент написания) - просто плохие обходные пути для чего-то, что идеально решается с помощью -isystem.

Если вы используете такую ​​систему сборки, как CMake или Meson, она автоматически установит для вас -I и -isystem.

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

person Mathias    schedule 28.11.2020
comment
Звучит интересно. Я не смогу сразу проверить, работает ли это. Но, может быть, в рождественские праздники. - person Knitschi; 29.11.2020
comment
Единственная проблема, с которой я столкнулся, - это как отключить ошибки и предупреждения для расширенных макросов из этих системных заголовков. - person Arsenal; 05.03.2021

Я не смог добиться того, чего хотел, с параметрами строки команды, поэтому я буду использовать комментарии // NOLINT в файлах cpp, которые были предложены принятым ответом.

Я также попробую выложить исправление в googletest.

Я обнаружил, что строки в параметрах -line-filter отфильтрованы. Но предоставление конкретных строк в любом случае не является реальным решением моей проблемы. Мне скорее нужен механизм подавления, как он реализован в Valgrind.

person Knitschi    schedule 05.10.2016