clang-tidy + cxx_modules

I have tried to enable the clang-tidy on cxx_module project using CMAKE_CLANG_TIDY. But it seems to fail and I don’t know if it is an issue in how cmake calls it or an issue on clang-tidy side. Here is a mwe:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(clang-tidy-issue
		LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API 2182bf5c-ef0d-489a-91da-49dbc3090d2a)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP TRUE)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT clang)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*")

add_library(test_library)
target_sources(test_library PUBLIC
		FILE_SET cxx_modules
		TYPE CXX_MODULES
		FILES
		test_module.ixx
		)
// test_module.ixx
module;

#include <string>

export module test_module;

export namespace test_module {
	consteval std::string_view version(){
		return "0.0.0";
	}
}

see C++20 modules API example - #3 by ClausKlein

So it’s supposed to be:

set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*,-llvmlibc-*,-fuchsia-*,-cppcoreguidelines-init-variables")

Maybe the missing llvmlibc-*?

# First find clang-tidy, this also allows users to provide hints
find_program(CLANG_TIDY NAMES clang-tidy)

# Then I have this which is essentially calls clang-tidy with warnings as errors (For CI)
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY};")

clang-tidy then finds the .clang-tidy file in my build without issue. So I don’t have to list all the checks in the CMake code.

From the clang-tidy docs:

# clang-tidy attempts to read configuration for each source file from a
#  .clang-tidy file located in the closest parent directory of the source
#  file. The .clang-tidy file is specified in YAML format. If any configuration
#  options have a corresponding command-line option, command-line option takes precedence.

The notes above are right, but it is a standalone working example only.

The real question is why does clang-tidy ends with an clang-diagnostic-error:

bash-3.2$ ninja -C build/
ninja: Entering directory `build/'
[0/1] Re-running CMake...
CMake Warning (dev) at CMakeLists.txt:70 (target_sources):
  CMake's C++ module support is experimental.  It is meant only for
  experimentation and feedback to CMake developers.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done (0.1s)
CMake Warning (dev):
  C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
  experimental.  It is meant only for compiler developers to try.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Generating done (0.0s)
-- Build files have been written to: /Users/clausklein/Workspace/cpp/cxx20/test/build
[4/8] Building CXX object CMakeFiles/foo.dir/foo.cxx.o
/Users/clausklein/Workspace/cpp/cxx20/test/foo.cxx:8:14: warning: class 'foo' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions,hicpp-special-member-functions]
export class foo {
             ^
/Users/clausklein/Workspace/cpp/cxx20/test/foo.cxx:8:14: warning: invalid case style for class 'foo' [readability-identifier-naming]
export class foo {
             ^~~
             Foo
/Users/clausklein/Workspace/cpp/cxx20/test/foo.cxx:15:11: warning: method 'helloworld' can be made static [readability-convert-member-functions-to-static]
void foo::helloworld() { std::cout << "hello world\n"; }
          ^
[7/8] Building CXX object CMakeFiles/hello.dir/main.cxx.o
FAILED: CMakeFiles/hello.dir/main.cxx.o 
/usr/local/Cellar/cmake/3.26.3/bin/cmake -E __run_co_compile --tidy="clang-tidy;-checks=*,-llvmlibc-*,-fuchsia-*,-cppcoreguidelines-init-variables;--extra-arg-before=--driver-mode=g++" --source=/Users/clausklein/Workspace/cpp/cxx20/test/main.cxx -- /usr/local/opt/llvm/bin/clang++   -std=c++20 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -MD -MT CMakeFiles/hello.dir/main.cxx.o -MF CMakeFiles/hello.dir/main.cxx.o.d @CMakeFiles/hello.dir/main.cxx.o.modmap -o CMakeFiles/hello.dir/main.cxx.o -c /Users/clausklein/Workspace/cpp/cxx20/test/main.cxx
/Users/clausklein/Workspace/cpp/cxx20/test/main.cxx:1:8: error: module 'foo' not found [clang-diagnostic-error]
import foo;
~~~~~~~^~~
1 error generated.
Error while processing /Users/clausklein/Workspace/cpp/cxx20/test/main.cxx.
Found compiler error(s).
ninja: build stopped: subcommand failed.
bash-3.2$ 

I got it!

-fprebuilt-module-path=CMakeFiles/foo.dir/ must be set:

bash-3.2$ pwd
/Users/clausklein/Workspace/cpp/cxx20/test/build

bash-3.2$ /usr/local/opt/llvm/bin/clang-tidy -extra-arg=-fprebuilt-module-path=CMakeFiles/foo.dir/ -p=/Users/clausklein/Workspace/cpp/cxx20/test/build /Users/clausklein/Workspace/cpp/cxx20/test/main.cxx
77759 warnings generated.
Suppressed 77759 warnings (77759 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
/usr/local/opt/llvm/bin/clang-tidy -extra-arg=-fprebuilt-module-path=CMakeFiles/foo.dir/ -p=/Users/clausklein/Workspace/cpp/cxx20/test/build /Users/clausklein/Workspace/cpp/cxx20/test/foo.cxx
/Users/clausklein/Workspace/cpp/cxx20/test/foo.cxx:15:11: warning: method 'helloworld' can be made static [readability-convert-member-functions-to-static]
void foo::helloworld() { std::cout << "hello world\n"; }
          ^
85168 warnings generated.
Suppressed 85167 warnings (85167 in non-user code).
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$ 

I have tried out these approaches and none of them worked. I realized I forgot to add the error encountered so:

====================[ Build | test_library | Debug ]============================
/usr/bin/cmake --build /clang-tidy-issue/cmake-build-debug --target test_library -j 14
[1/2] Building CXX object CMakeFiles/test_library.dir/test_module.ixx.o
FAILED: CMakeFiles/test_library.dir/test_module.ixx.o CMakeFiles/test_library.dir/test_module.pcm 
/usr/bin/cmake -E __run_co_compile --tidy="clang-tidy;-checks=*,-llvmlibc-*,-fuchsia-*,-cppcoreguidelines-init-variables;--extra-arg-before=--driver-mode=g++" --source=/clang-tidy-issue/test_module.ixx -- /usr/bin/clang++   -g -std=c++20 -fcolor-diagnostics -MD -MT CMakeFiles/test_library.dir/test_module.ixx.o -MF CMakeFiles/test_library.dir/test_module.ixx.o.d @CMakeFiles/test_library.dir/test_module.ixx.o.modmap -o CMakeFiles/test_library.dir/test_module.ixx.o -c /clang-tidy-issue/test_module.ixx
error: unable to handle compilation, expected exactly one compiler job in '' [clang-diagnostic-error]
warning: /home/lecris/CLionProjects/clang-tidy-issue/test_module.ixx: 'linker' input unused [clang-diagnostic-unused-command-line-argument]
warning: argument unused during compilation: '-c' [clang-diagnostic-unused-command-line-argument]
warning: argument unused during compilation: '-g' [clang-diagnostic-unused-command-line-argument]
Error while processing /clang-tidy-issue/test_module.ixx.
Found compiler error(s).
ninja: build stopped: subcommand failed.

Details of setup:

  • Fedora 38
  • clang-tidy: 16.0.0
  • cmake: 3.26.3

It seems to be an issue with the file name suffix .ixx, .cxx seems to work sometimes

Dit you use my updated CMakeLists.txt?

Yes, I have tested your cmakelists and it worked perfectly fine, which puzzled me. But I managed to systematically reproduce the issue when I use .ixx suffix.

It seems the extensions are not in clnag-tidy 16 yet:

I have tested that commit and that doesn’t solve the issue either :frowning:

See also discussion here: