Skip to content

Conversation

felix642
Copy link
Contributor

@felix642 felix642 commented Oct 7, 2024

When using clang-tidy from a compilation database, some options might not be recognized by clang if the compilation database was generated for another compiler. This forces the user to add conditional code in their CMakeLists.txt to remove those arguments when using clang-tidy.
A new option was added to the .clang-tidy config to remove those unknown flags without the need to generate a second compilation_commands.json

Fixes #108455

When using clang-tidy from a compilation database, some options might not be
recognized by clang if the compilation database was generated for another compiler.
This forces the user to add conditional code in their CMakeLists.txt to remove
those arguments when using clang-tidy.
A new option was added to the .clang-tidy config to remove
those unknown flags without the need to generate a second compilation_commands.json

Fixes llvm#108455
@llvmbot
Copy link
Member

llvmbot commented Oct 7, 2024

@llvm/pr-subscribers-clang-tidy

@llvm/pr-subscribers-clang-tools-extra

Author: Félix-Antoine Constantin (felix642)

Changes

When using clang-tidy from a compilation database, some options might not be recognized by clang if the compilation database was generated for another compiler. This forces the user to add conditional code in their CMakeLists.txt to remove those arguments when using clang-tidy.
A new option was added to the .clang-tidy config to remove those unknown flags without the need to generate a second compilation_commands.json

Fixes #108455


Full diff: https://github.com/llvm/llvm-project/pull/111453.diff

6 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/ClangTidy.cpp (+24)
  • (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.cpp (+5)
  • (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.h (+4)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+3)
  • (modified) clang-tools-extra/docs/clang-tidy/index.rst (+3)
  • (added) clang-tools-extra/test/clang-tidy/infrastructure/compilation-args-to-ignore.cpp (+9)
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 62f9d19b2a362f..4bd921b819260e 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -552,6 +552,30 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
         return AdjustedArgs;
       };
 
+  // Remove unwanted arguments passed to the compiler
+  ArgumentsAdjuster CompilationArgsToIgnore =
+      [&Context](const CommandLineArguments &Args, StringRef Filename) {
+        ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
+        CommandLineArguments AdjustedArgs = Args;
+
+        if (Opts.CompilationArgsToRemoveRegex) {
+          for (StringRef ArgToIgnore : *Opts.CompilationArgsToRemoveRegex) {
+            llvm::Regex ArgToIgnoreRegex(ArgToIgnore);
+            AdjustedArgs.erase(
+                std::remove_if(AdjustedArgs.begin(), AdjustedArgs.end(),
+                               [&](llvm::StringRef Arg) {
+                                 return Arg.starts_with("-") &&
+                                        Arg != "-fsyntax-only" &&
+                                        ArgToIgnoreRegex.match(Arg);
+                               }),
+                AdjustedArgs.end());
+          }
+        }
+
+        return AdjustedArgs;
+      };
+
+  Tool.appendArgumentsAdjuster(CompilationArgsToIgnore);
   Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
   Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
   Context.setEnableProfiling(EnableCheckProfile);
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
index 445c7f85c900c6..32112db07b09d2 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
@@ -174,6 +174,8 @@ template <> struct MappingTraits<ClangTidyOptions> {
                    Options.ExcludeHeaderFilterRegex);
     IO.mapOptional("FormatStyle", Options.FormatStyle);
     IO.mapOptional("User", Options.User);
+    IO.mapOptional("CompilationArgsToRemoveRegex",
+                   Options.CompilationArgsToRemoveRegex);
     IO.mapOptional("CheckOptions", Options.CheckOptions);
     IO.mapOptional("ExtraArgs", Options.ExtraArgs);
     IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
@@ -198,6 +200,7 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
   Options.SystemHeaders = false;
   Options.FormatStyle = "none";
   Options.User = std::nullopt;
+  Options.CompilationArgsToRemoveRegex = std::nullopt;
   for (const ClangTidyModuleRegistry::entry &Module :
        ClangTidyModuleRegistry::entries())
     Options.mergeWith(Module.instantiate()->getModuleOptions(), 0);
@@ -238,6 +241,8 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
   overrideValue(SystemHeaders, Other.SystemHeaders);
   overrideValue(FormatStyle, Other.FormatStyle);
   overrideValue(User, Other.User);
+  overrideValue(CompilationArgsToRemoveRegex,
+                Other.CompilationArgsToRemoveRegex);
   overrideValue(UseColor, Other.UseColor);
   mergeVectors(ExtraArgs, Other.ExtraArgs);
   mergeVectors(ExtraArgsBefore, Other.ExtraArgsBefore);
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
index 85d5a02ebbc1bc..0ccdde1ff3fc31 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
@@ -110,6 +110,10 @@ struct ClangTidyOptions {
   /// comments in the relevant check.
   std::optional<std::string> User;
 
+  /// \brief Remove command line arguments sent to the compiler matching this
+  /// regex.
+  std::optional<std::vector<std::string>> CompilationArgsToRemoveRegex;
+
   /// Helper structure for storing option value with priority of the value.
   struct ClangTidyValue {
     ClangTidyValue() = default;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3e051c7db6adcc..ea3ad5356a897c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -106,6 +106,9 @@ Improvements to clang-tidy
 - Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise
   happening on certain platforms when interrupting the script.
 
+- Improved :program:`clang-tidy` by adding the option `CompilationArgsToRemoveRegex`
+  to remove arguments sent to the compiler when invoking clang-tidy.
+
 New checks
 ^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst
index e38141bdb8be1f..65d96b29079f23 100644
--- a/clang-tools-extra/docs/clang-tidy/index.rst
+++ b/clang-tools-extra/docs/clang-tidy/index.rst
@@ -303,6 +303,8 @@ An overview of all the command-line options:
                                    example, to place the correct user name in
                                    TODO() comments in the relevant check.
     WarningsAsErrors             - Same as '--warnings-as-errors'.
+    CompilationArgsToRemoveRegex - List of arguments to remove from the command
+                                   line sent to the compiler.
 
     The effective configuration can be inspected using --dump-config:
 
@@ -312,6 +314,7 @@ An overview of all the command-line options:
       WarningsAsErrors:    ''
       HeaderFileExtensions:         ['', 'h','hh','hpp','hxx']
       ImplementationFileExtensions: ['c','cc','cpp','cxx']
+      CompilationArgsToRemoveRegex: ['-Werror', '-f.*']
       HeaderFilterRegex:   ''
       FormatStyle:         none
       InheritParentConfig: true
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/compilation-args-to-ignore.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/compilation-args-to-ignore.cpp
new file mode 100644
index 00000000000000..33a42d416e75a7
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/compilation-args-to-ignore.cpp
@@ -0,0 +1,9 @@
+// RUN: not clang-tidy %s -- -fnot-an-option | FileCheck %s -check-prefix=INVALID-A
+// RUN: clang-tidy %s --config="{CompilationArgsToRemoveRegex: ['-fnot-an-option']}" -- -fnot-an-option
+// RUN: not clang-tidy %s --config="{CompilationArgsToRemoveRegex: ['-f.*']}" -- -fnot-an-option -invalid-option | FileCheck %s -check-prefix=INVALID-B
+// RUN: clang-tidy %s --config="{CompilationArgsToRemoveRegex: ['-f.*', '-invalid-option']}" -- -fnot-an-option -fnot-another-option -finvalid-option -invalid-option
+// RUN: not clang-tidy %s --config="{CompilationArgsToRemoveRegex: ['\$invalid-option']}" -- -finvalid-option | FileCheck %s -check-prefix=INVALID-C
+
+// INVALID-A: error: unknown argument: '-fnot-an-option' [clang-diagnostic-error]
+// INVALID-B: error: unknown argument: '-invalid-option' [clang-diagnostic-error]
+// INVALID-C: error: unknown argument: '-finvalid-option' [clang-diagnostic-error]
\ No newline at end of file

- Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise
happening on certain platforms when interrupting the script.

- Improved :program:`clang-tidy` by adding the option `CompilationArgsToRemoveRegex`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move it after previous clang-tidy entry. Also ":option:CompilationArgsToRemoveRegex"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EugeneZelenko, I might be wrong, but since the option is only mentioned in index.rst, I don't think I can use :option: in the Release notes.

@emmett2020
Copy link

This would be a big improvement for users who using another compiler.
Thank you.

@nicovank
Copy link
Contributor

Just for discussion:

  1. Almost equivalent to sed -i -E 's/-f[[:alnum:][:punct:]]*//g; s/-Werror//g' compile_commands.json?
  2. What if a -f... argument is needed while others should be ignored? The regex might get complicated in some cases.
  3. I don't know how difficult this is at all: maybe an --ignore-unknown-flags option that emits a warning when unknown flags are encountered instead of failing and ignores only those could be another solution?

@carlosgalvezp
Copy link
Contributor

I too have the feeling that this feature is a responsibility of the build system, not of clang-tidy. What do you think @AaronBallman ?

@AaronBallman
Copy link
Collaborator

I too have the feeling that this feature is a responsibility of the build system, not of clang-tidy. What do you think @AaronBallman ?

If I understand correctly, this situation is when CMake is configured for another compiler (e.g., GCC) but is also instructed to run clang-tidy, but then CMake hands clang-tidy the command line arguments it was handing to GCC, and clang-tidy is giving hard errors for the unknown command line arguments.

Clang itself ignores some unknown command line options while erring on others (it seems to be - vs -- which decides this): https://godbolt.org/z/f1T8hjP69 Would it make sense for clang-tidy to follow suit and ignore unknown options specified with -? That would help in the modules case, but wouldn't help for the CUDA case mentioned in the issue.

CC @MaskRay @jansvoboda11 for additional opinions (and why does clang warn in some cases but err in others, is that intentional?)

@felix642
Copy link
Contributor Author

If I understand correctly, this situation is when CMake is configured for another compiler (e.g., GCC) but is also instructed to run clang-tidy, but then CMake hands clang-tidy the command line arguments it was handing to GCC, and clang-tidy is giving hard errors for the unknown command line arguments.

Exactly. This option was added to handle this situation similarly to what Clangd already does. They currently have an option Remove (https://clangd.llvm.org/config.html#remove) which removes arguments from the command line before compiling the source file.

Currently, clang-tidy only supports adding arguments through the ExtraArgs options, but does not allow removing arguments and the user either has to add conditional statements to allow only flags recognized by Clang when running CMake or postprocess the compile_commands.json file to remove those unknown flags.

I think the ideal solution would be to support this feature directly with the build system as highlighted by @carlosgalvezp and maybe add an option to clang-tidy as suggested by @nicovank --ignore-unknown-flags to allow clang-tidy to run with unrecognized flags.

I have added the argument in clang-tidy since this was fairly simple to support and was already in line with what was done in other tools (Clangd). I don't mind having a more in-depth look into the clang's codebase to see how those unknown arguments could be handled directly by the build system.

@carlosgalvezp
Copy link
Contributor

carlosgalvezp commented Nov 22, 2024

The way we do it at our place is that we have 2 toolchains, one for GCC and one for Clang, each with separate sets of flags. We assume that being able to compile with Clang is a prerequisite to running clang-tidy. For example, even if you have only -Wall, Clang (via clang-tidy) could introduce a warning that GCC doesn't have, so you'd get Clang compiler errors via clang-tidy (clang-diagnostic-error), which is confusing.

Once that's satisfied, we can run clang-tidy based on the Clang toolchain - this way we ensure all the flags are valid for clang-tidy (since they are for Clang).

But I understand if having 2 parallel toolchains can be costly to maintain.

Alternatively, you could have a simple wrapper around clang-tidy that filters out the unknown flags, and tell CMake to run that wrapper instead.

@HerrCai0907
Copy link
Contributor

HerrCai0907 commented Nov 23, 2024

I too have the feeling that this feature is a responsibility of the build system, not of clang-tidy

II think a would be great if clang-tidy has an out of the box experience. some project may don't support clang compiler due to CI (e.g. clang and gcc will warning different thing in -Wall. But they still can get benefit from clang-tidy. I think it is good to support this function in general. But in which way it need to be discussed. I personally do not like regex solution. Instead, supporting unknown arguments looks better.

@5chmidti
Copy link
Contributor

I think the regex solution might be brittle/not clean from a user perspective, with potential for a small amount of maintenance. Instead, I would also suggest a flag to error, warn, or silence these, defaulting to a warning.

Using compile_commands.json from a build configured for another compiler should provide a good experience.

  • problem: --flag is not known to Clang, but would enable some part through a macro define -> missed analysis of important part of the code base?
  • fine: --other is not known to Clang, but also not important to analysis because it does something during code-gen, etc.

The --flag example is why I'd be a bit worried about fully silencing these unknown flags, instead of a downgrade to a warning.

@MaskRay
Copy link
Member

MaskRay commented Nov 30, 2024

I think the regex solution might be brittle/not clean from a user perspective, with potential for a small amount of maintenance. Instead, I would also suggest a flag to error, warn, or silence these, defaulting to a warning.

Using compile_commands.json from a build configured for another compiler should provide a good experience.

  • problem: --flag is not known to Clang, but would enable some part through a macro define -> missed analysis of important part of the code base?
  • fine: --other is not known to Clang, but also not important to analysis because it does something during code-gen, etc.

The --flag example is why I'd be a bit worried about fully silencing these unknown flags, instead of a downgrade to a warning.

Second this. The option can ignore certain driver errors (unknown argument).

@felix642 felix642 closed this Jan 3, 2025
@kwalker-plxs
Copy link

@felix642, I have ran into some challenges with clang-tidy running on a GCC generated compilation database with C++ 20 module support (same issue as in #108455). I do not see an obvious reason why this implementation was not merged. What was the motivation for closing without merging?

@felix642
Copy link
Contributor Author

felix642 commented Oct 3, 2025

@kwalker-plxs
Please have a look at other contributor's concerns on this PR. The main issue was that setting up this option would be non-trivial and might get pretty complicated if they are multiple flags that we need to remove. It might also remove some unexpected arguments without the user noticing and impact the quality of the clang-tidy's inspection if not setup properly.

In my opinion, and after reading this PR's comments, a better approach would be to add an option to downgrade the errors clang-tidy currently emits when it doesn't recognize a compilation flag. That way the user could downgrade the errors to warnings if they want to have a feedback on which arguments were ignored or completely ignore them if they don't care.

This would be much easier to maintain from a user's perspective since a change to the GCC compilation wouldn't impact the clang-tidy analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ignore unknown flags in clang-tidy