I am currently in the process of changing my project from just msvc to clang-cl. The source code is almost exclusively c++17, apart from some thirdparty libs i am using.
The app complies and runs fine with clang-cl, but i am having problems with getting clang-tidy to work. One of the later goals will be to use in clang-tidy in a gitlab workflow, but currently i am trying to get it to work locally, so i am running commands from console.
I have read the and followed the instructions in the LLVM documentation, and i also checked that yes, the compile command are indeed exported into compile_commands.json. I also checked the CMakeCache and it also seems to be in order.
Tools used:
Target platform is Windows 11. Here is a Minimal Reproducible Example:
C:/Projects/MRE/CMakePresets.json
{
"version": 8,
"configurePresets": [
{
"name": "debug",
"displayName": "Debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cmakeExecutable": "C:\\CMake\\bin\\cmake.exe",
"cacheVariables": {
"CMAKE_MAKE_PROGRAM": "C:\\ninja\\ninja.exe",
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_C_FLAGS": "-m32",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_CXX_FLAGS": "-m32",
"CMAKE_EXE_LINKER_FLAGS": "/errorlimit:0 /debug:full",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "release",
"displayName": "Release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cmakeExecutable": "C:\\CMake\\bin\\cmake.exe",
"cacheVariables": {
"CMAKE_MAKE_PROGRAM": "C:\\ninja\\ninja.exe",
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_C_FLAGS": "-m32",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_CXX_FLAGS": "-m32",
"CMAKE_EXE_LINKER_FLAGS": "/errorlimit:0 /debug:full",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "release",
"configurePreset": "release"
}
]
}
C:/Projects/MRE/CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
set(TARGET_NAME MRE_Main)
project(MRE VERSION 0.0.1 LANGUAGES C CXX)
set(SOURCES
"Source/main.cpp"
)
add_executable(${TARGET_NAME} ${SOURCES})
C:/Projects/MRE/Source/main.cpp
#include <iostream>
int main() {
std::cout << "Hello World!";
return 0;
}
Building the project:
C:/Projects/MRE
folder, or wherever your copy is.CMakePresets.json
, to point to your installations.Alternatively you can build from console:
CMakePresets.json
, to point to your installations.cd C:\Projects\MRE
set PATH=C:\CMake\bin\;C:\ninja\;%PATH%
(Rewrite paths to point to your installations)cmake --preset debug
cmake --preset --build debug
Steps to reproduce:
cd C:\Projects\MRE
set PATH=C:\CMake\bin\;C:\ninja\;%PATH%
(Again, rewrite paths to point to your installations)clang-tidy -p=./build/debug/ Source/main.cpp
For me output is:
C:\Projects\MRE>clang-tidy -p=./build/debug/ Source/main.cpp
2 errors generated.
Error while processing C:\Projects\MRE\Source\main.cpp.
error: no such file or directory: '-fsyntax-only'; did you mean '-fsyntax-only'? [clang-diagnostic-error]
error: no such file or directory: '-resource-dir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\lib\clang\12.0.0'; did you mean '-resource-dir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\lib\clang\12.0.0'? [clang-diagnostic-error]
Found compiler error(s).
I checked on 2 different computers with the exact same tools installed, and i get the same results.
To me it seems the problem is an extra ';' at the end of complier arguments, but i can not find anyone with the same problem, and have no clue how to fix it.
I also checked for .clang-tidy
files, and found none on the path. As a drastic measure, I deleted every single .clang-tidy
file, no matter where it was. Still the same result. Not sure where the -fsyntax-only flag comes from.
Edit:
Here is the requested clang-tidy --dump-config
.
---
Checks: 'clang-diagnostic-*,clang-analyzer-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: none
User: user
CheckOptions:
- key: llvm-else-after-return.WarnOnConditionVariables
value: '0'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons
value: '0'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: '0'
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: '1'
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
value: '1'
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: llvm-qualified-auto.AddConstToQualified
value: '0'
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: llvm-else-after-return.WarnOnUnfixable
value: '0'
- key: google-readability-function-size.StatementThreshold
value: '800'
...
The error is due to a bug in LLVM-12 and earlier relating to how it
handles --
in the compile_commands.json
command line. It was fixed
in LLVM-13 or LLVM-14. It does not involve Visual Studio. It involves
cmake
only because cmake
creates compile_commands.json
with the
--
switch that provokes the bug. The bug manifests on both Windows
and Linux.
Create an empty source file, and a compile_commands.json
that has a
command line with --
before the file name. Then run clang-tidy
on
that file.
Example showing the bug when using LLVM-11:
$ cat test.cpp
$ cat compile_commands.json
[
{
"directory": "/home/scott/wrk/learn/clang/clang-tidy-syntax-only-bug",
"command": "clang.exe -c -- test.cpp",
"file": "/home/scott/wrk/learn/clang/clang-tidy-syntax-only-bug/test.cpp"
}
]
$ ~/opt/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04/bin/clang-tidy test.cpp
2 errors generated.
Error while processing /home/scott/wrk/learn/clang/clang-tidy-syntax-only-bug/test.cpp.
error: no such file or directory: '-fsyntax-only'; did you mean '-fsyntax-only'? [clang-diagnostic-error]
error: no such file or directory: '-resource-dir=/home/scott/opt/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04/lib/clang/11.0.1'; did you mean '-resource-dir=/home/scott/opt/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04/lib/clang/11.0.1'? [clang-diagnostic-error]
Found compiler error(s).
Exit 1
Example showing that it works with LLVM-14, executed right after the previous command:
$ ~/opt/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin/clang-tidy test.cpp
(No output, exit code 0.)
Either upgrade to LLVM-14 or later, or remove the --
in the command
line in compile_commands.json
(and arrange to keep removing it despite
cmake
putting it there).
As it happens, VS 2019 ships with LLVM-12, while VS 2022 ships with
LLVM-17, so upgrading to VS 2022 also solves the problem when using
its clang-tidy
.
This bug was filed against LLVM github as Issue 49639: Clang-Tidy reports wrong command line argument even though it is not specified at all back in May 2021. However, the cause couldn't be determined and it was eventually closed as invalid.
-fsyntax-only
come from?It comes from clang-tidy
itself, in
Tooling.cpp:
static std::vector<std::string>
getSyntaxOnlyToolArgs(const Twine &ToolName,
const std::vector<std::string> &ExtraArgs,
StringRef FileName) {
std::vector<std::string> Args;
Args.push_back(ToolName.str());
Args.push_back("-fsyntax-only");
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
Args.push_back(FileName.str());
return Args;
}
The -resource-dir
argument also comes from the same file:
static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
void *MainAddr) {
// Allow users to override the resource dir.
for (StringRef Arg : Args)
if (Arg.startswith("-resource-dir"))
return;
// If there's no override in place add our resource dir.
Args.push_back("-resource-dir=" +
CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
}
These options were evidently being placed after the --
that was
found in compile_commands.json
, and therefore interpreted as input
file names by the rest of the driver infrastructure. That causes the
err_drv_no_such_file_with_suggestion
error message to be printed:
def err_drv_no_such_file_with_suggestion : Error<
"no such file or directory: '%0'; did you mean '%1'?">;
I don't know. I couldn't find any other issues that seemed related (PR 66553 is in the vicinity but is too recent), and didn't dig deeper than what's reported above to find the bug itself.