How to prevent clang-tidy to check sources added with FetchContents?

The example use cxxopts which was fetched with CPM.cmake using FetchContents:

builddriver executing: 'run-clang-tidy.py -p build/standalone -checks=-*,modernize-*,misc-*,hicpp-*,cert-*,readability-*,portability-*,performance-*,google-* standalone'
Compilation SUCCEED in 8.628576 seconds
Number of warnings: 238
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='51', severity='warning', message="declaration uses identifier 'CXXOPTS__VERSION_MAJOR', which is a reserved identifier [cert-dcl37-c,cert-dcl51-cpp]", column='9')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='52', severity='warning', message="declaration uses identifier 'CXXOPTS__VERSION_MINOR', which is a reserved identifier [cert-dcl37-c,cert-dcl51-cpp]", column='9')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='53', severity='warning', message="declaration uses identifier 'CXXOPTS__VERSION_PATCH', which is a reserved identifier [cert-dcl37-c,cert-dcl51-cpp]", column='9')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='212', severity='warning', message="use 'using' instead of 'typedef' [modernize-use-using]", column='3')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='216', severity='warning', message='use a trailing return type for this function [modernize-use-trailing-return-type]', column='3')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='223', severity='warning', message='use a trailing return type for this function [modernize-use-trailing-return-type]', column='3')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='230', severity='warning', message='use a trailing return type for this function [modernize-use-trailing-return-type]', column='3')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='230', severity='warning', message="non-const reference parameter 's', make it const or use a pointer [google-runtime-references]", column='23')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='230', severity='warning', message="the parameter 'a' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]", column='33')
WarningErrorEntry(path='/Users/clausklein/.cache/CPM/cxxopts/d5c328043c71a6a5670c290d8646b093756db962/include/cxxopts.hpp', lineno='232', severity='warning', message='passing result of std::move() as a const reference argument; no move will actually happen [hicpp-move-const-arg,performance-move-const-arg]', column='21')
# . . .

Hint: all headers included with #include<...> and found with -isystem are suppressed by default (at least by gcc and clang compilers)

When the library is installed and used with find_package(cxxopts) it works fine.

1 Like

This fix it temporarily:

perl -i.bak -pe 's#-I($CPM_SOURCE_CACHE)#-isystem $1#g' build/install/compile_commands.json
run-clang-tidy.py -p build/install source
# . . .
# no warnings

You can wrap your FetchContent call with:

foreach (lang IN ITEMS C CXX)
  set("CMAKE_${lang}_CLANG_TIDY_save" "${CMAKE_${lang}_CLANG_TIDY}")
  set("CMAKE_${lang}_CLANG_TIDY" "")
endforeach ()
FetchContent()
foreach (lang IN ITEMS C CXX)
  set("CMAKE_${lang}_CLANG_TIDY" "${CMAKE_${lang}_CLANG_TIDY_save}")
endforeach ()

unfortunately it doesn’t help :frowning_face:

in general, there are 2 problems to solve

1.) the generated build files i.e. rules.nina

rule CXX_COMPILER__fmt_Debug
  depfile = $DEP_FILE
  deps = gcc
  command = /usr/local/Cellar/cmake/3.19.5/bin/cmake -E __run_co_compile --launcher=ccache --tidy="clang-tidy;--extra-arg-before=--driver-mode=g++" --source=$in -- /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ $DEFINES $INCLUDES $FLAGS -MMD -o $out -c $in
  description = Building CXX object $out

2.) the compile_commands.json file

see How to prevent clang-tidy to check sources added with FetchContents? - #2 by ClausKlein

@craig.scott do you have any idea about it?

I would have expected @ben.boeckel’s suggestion to work. I’m not able to dig deeper into this right now, sorry.

Is this the same as Disable warning on fetched projects?

See the solution from the first post. This is what I do for every library. It works great. So if you compile your project ABC it will use isystem for your lib XYZ (gcc / clang).
I’m not quite sure about compiler warnings though. In the first run it will also compile XYZ and therefore might show some compiler warnings. I haven’t yet investigated this.
But it’s only the first time anyway, so no harm really. And it seems to be compiler warnings and not clang tidy

CMake 3.25 is adding a new SYSTEM target property. It is initialised from a corresponding SYSTEM directory property, and both add_subdirectory() and FetchContent_Declare() are gaining an associated SYSTEM keyword as well. I’m starting to wonder if we may want to disable all linters, checkers and verification tasks on targets that have their SYSTEM property set to true.

Maybe we could have a dedicated target property for whether checkers should be enabled in case someone wants to enable checking on a target marked SYSTEM. We could fall back to the SYSTEM property if that dedicated property isn’t set.

I haven’t thought this through, nor have an appreciation for how easy or hard it would be to implement, but it seemed worth mentioning for further discussion.

2 Likes

+1 for this idea!!

1 Like

Apologies for reviving this thread. I thought adding the SYSTEM property to FetchContent_Declare for googletest would be silencing the clang-tidy messages arising from googletest but this was not the case. It took me a bit of time to sort it out. I hope this can help others. My workaround is now to set CXX_CLANG_TIDY (not CMAKE_CXX_CLANG_TIDY) to be empty for the gtest and gmock targets.

Here is a MWE illustrating the workaround:

cmake_minimum_required(VERSION 3.25)
project(gtest-tidy-mwe)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Set up clang-tidy
find_program(CLANG_TIDY_EXE NAMES clang-tidy PATHS /opt/homebrew/opt/llvm/bin/)
if(NOT CLANG_TIDY_EXE)
  message(ERROR "clang-tidy not found. Skipping corresponding checks.")
else()
  set(CMAKE_CXX_CLANG_TIDY 
    ${CLANG_TIDY_EXE};
    -header-filter=.;
    -checks=-*,portability-*,bugprone-*,readability-;
    -warnings-as-errors=*)
endif()

# Set up googletest
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        750d67d809700ae8fca6d610f7b41b71aa161808
  SYSTEM
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# Silence clang-tidy warnings from googletest
set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "")
set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "")
set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "")
set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "")

# Create dummy main
file(WRITE ${CMAKE_BINARY_DIR}/gtest-tidy-mwe.cpp
  "#include <gtest/gtest.h>\n"
  "TEST(HelloTest, BasicAssertions) {\n"
  "  EXPECT_EQ(7 * 6, 42);\n"
  "}\n"
)

add_executable(gtest-tidy-mwe
  ${CMAKE_BINARY_DIR}/gtest-tidy-mwe.cpp
)

target_link_libraries(gtest-tidy-mwe PRIVATE GTest::gtest GTest::gtest_main)

It works, but you are expecting to much!

With this CMakeLists.txt you compile everything with clang-tidy, the gtest sources too!

If you build all and check your example source file only, it works as you want:

/usr/local/opt/llvm/bin/clang-tidy -p=/Users/clausklein/Workspace/cpp/test/cmake-build-test-x86_64-Debug/install /Users/clausklein/Workspace/cpp/test/cmake-build-test-x86_64-Debug/install/gtest-tidy-mwe.cpp
81565 warnings generated.
Suppressed 81622 warnings (81565 in non-user code, 57 NOLINT).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
bash-3.2$ 
cmake_minimum_required(VERSION 3.25...3.26)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(gtest-tidy-mwe LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Set up clang-tidy
find_program(CLANG_TIDY_EXE NAMES clang-tidy PATHS /opt/homebrew/opt/llvm/bin/)
if(NOT CLANG_TIDY_EXE)
  message(ERROR "clang-tidy not found. Skipping corresponding checks.")
else()
  set(CLANG_TIDY_CMD_LINE ${CLANG_TIDY_EXE}; -header-filter=.;
    -checks=-*,modernize-*,bugprone-*,hicpp-*,performance-*,portability-*,readability-;
    -warnings-as-errors=*
  )
endif()

# Enable clang-tidy for target
macro(target_enable_clang_tidy TARGET)
  set_target_properties(${TARGET} PROPERTIES C_CLANG_TIDY "${CLANG_TIDY_CMD_LINE}")
  set_target_properties(${TARGET} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_CMD_LINE}")
endmacro()

# Set up googletest
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG 750d67d809700ae8fca6d610f7b41b71aa161808
  SYSTEM
)

# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)


# Create dummy main
file(WRITE ${CMAKE_BINARY_DIR}/gtest-tidy-mwe.cpp
    "#include <gtest/gtest.h>\n" "TEST(HelloTest, BasicAssertions) {\n"
    "  EXPECT_EQ(7 * 6, 42);\n" "}\n"
)

add_executable(gtest-tidy-mwe ${CMAKE_BINARY_DIR}/gtest-tidy-mwe.cpp)

target_link_libraries(gtest-tidy-mwe PRIVATE GTest::gtest GTest::gtest_main)

# enable clang-tidy for my target only!
target_enable_clang_tidy(gtest-tidy-mwe)

enable_testing()
add_test(NAME gtest-tidy-mwe COMMAND gtest-tidy-mwe)

install(TARGETS gtest-tidy-mwe)
include(cpack)
2 Likes

P.S: I never set

I always set

Many thanks! I figured it out at the same time and edited my previous post.

In my earlier comment, I only suggested that the SYSTEM property could be made to apply to linters, checkers and verification tasks as well, but to my knowledge, there hasn’t been a formal proposal or anyone working on it yet.

2 Likes

I’ve recently tried this, and I’m pretty sure run-clang-tidy is not respecting it. I have multiple projects imported via FetchContent and even with SYSTEM they still show up in my clang-tidy run.

If you mean this, than you are right, That is only to prevent the pre-compile-step!

Ah, shame, so that means this won’t work with run-clang-tidy?