Skip to content

Conversation

felix642
Copy link
Contributor

@felix642 felix642 commented Oct 6, 2025

This is another attempt at fixing #108455. Please see the initial discussion in the following PR: #111453

This parameter is used to suppress the Unknown argument '...' error that clang will emit whenever it encounters an unknown argument.

This is probably an error to make sure the user fixes it's mistake by either removing the argument or renaming it, but there are some cases where it's not possible to fix the issue.
For instance, CMake now injects gcc-specific arguments in the clang-tidy command that breaks static-analysis
(https://gitlab.kitware.com/cmake/cmake/-/issues/26283)

This will also allow users to run clang-tidy / clangd on a gcc-based project without the need to maintain two separate build commands to run llvm-based tools.

By enabling this parameter, the user is able to downgrade the error to a warning (unknown-argument) that he can further silence using the -Qunused-arguments flag if needed.

Fixes: #108455

This parameter is used to suppress the ``Unknown argument '...'`` error
that clang will emit whenever it encounters an unknown argument.

This is probably an error to make sure the user fixes it's mistake by
either removing the argument or renaming it, but there are some cases
where it's not possible to fix the issue.
For instance, CMake now injects gcc-specific arguments in the clang-tidy
command that breaks static-analysis
(https://gitlab.kitware.com/cmake/cmake/-/issues/26283)

This will also allow users to run clang-tidy / clangd on a
gcc-based project without the need to maintain two separate
build commands to run llvm-based tools.

By enabling this parameter, the user is able to downgrade the error to a
warning (unknown-argument) that he can further silence using the
``-Qunused-arguments`` flag if needed.

Fixes: llvm#108455
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Oct 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 6, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-driver

Author: Félix-Antoine Constantin (felix642)

Changes

This is another attempt at fixing #108455. Please see the initial discussion in the following PR: #111453

This parameter is used to suppress the Unknown argument '...' error that clang will emit whenever it encounters an unknown argument.

This is probably an error to make sure the user fixes it's mistake by either removing the argument or renaming it, but there are some cases where it's not possible to fix the issue.
For instance, CMake now injects gcc-specific arguments in the clang-tidy command that breaks static-analysis
(https://gitlab.kitware.com/cmake/cmake/-/issues/26283)

This will also allow users to run clang-tidy / clangd on a gcc-based project without the need to maintain two separate build commands to run llvm-based tools.

By enabling this parameter, the user is able to downgrade the error to a warning (unknown-argument) that he can further silence using the -Qunused-arguments flag if needed.

Fixes: #108455


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

4 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+3)
  • (modified) clang/lib/Driver/Driver.cpp (+22-20)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+11-9)
  • (modified) clang/test/Driver/unsupported-option.c (+4)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 2ef609831637e..de0d32f68253a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1011,6 +1011,9 @@ def : Flag<["-"], "Xcompiler">, IgnoredGCCCompat;
 def Z_Flag : Flag<["-"], "Z">, Group<Link_Group>;
 def all__load : Flag<["-"], "all_load">;
 def allowable__client : Separate<["-"], "allowable_client">;
+def allow_unrecognized_arguments : Flag<["--"], "allow-unrecognized-arguments">,
+    Visibility<[ClangOption, CLOption]>,
+    HelpText<"Ignore unrecognized command-line arguments instead of reporting an error.">;
 def ansi : Flag<["-", "--"], "ansi">, Group<CompileOnly_Group>;
 def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">;
 def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 85a1335785542..4f2ac9ba8271c 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -303,29 +303,31 @@ InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings,
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_UNKNOWN)) {
-    unsigned DiagID;
-    auto ArgString = A->getAsString(Args);
-    std::string Nearest;
-    if (getOpts().findNearest(ArgString, Nearest, VisibilityMask) > 1) {
-      if (!IsCLMode() &&
-          getOpts().findExact(ArgString, Nearest,
-                              llvm::opt::Visibility(options::CC1Option))) {
-        DiagID = diag::err_drv_unknown_argument_with_suggestion;
-        Diags.Report(DiagID) << ArgString << "-Xclang " + Nearest;
+  if (!Args.hasArg(options::OPT_allow_unrecognized_arguments)) {
+    for (const Arg *A : Args.filtered(options::OPT_UNKNOWN)) {
+      unsigned DiagID;
+      auto ArgString = A->getAsString(Args);
+      std::string Nearest;
+      if (getOpts().findNearest(ArgString, Nearest, VisibilityMask) > 1) {
+        if (!IsCLMode() &&
+            getOpts().findExact(ArgString, Nearest,
+                                llvm::opt::Visibility(options::CC1Option))) {
+          DiagID = diag::err_drv_unknown_argument_with_suggestion;
+          Diags.Report(DiagID) << ArgString << "-Xclang " + Nearest;
+        } else {
+          DiagID = IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl
+                              : diag::err_drv_unknown_argument;
+          Diags.Report(DiagID) << ArgString;
+        }
       } else {
-        DiagID = IsCLMode() ? diag::warn_drv_unknown_argument_clang_cl
-                            : diag::err_drv_unknown_argument;
-        Diags.Report(DiagID) << ArgString;
+        DiagID = IsCLMode()
+                     ? diag::warn_drv_unknown_argument_clang_cl_with_suggestion
+                     : diag::err_drv_unknown_argument_with_suggestion;
+        Diags.Report(DiagID) << ArgString << Nearest;
       }
-    } else {
-      DiagID = IsCLMode()
-                   ? diag::warn_drv_unknown_argument_clang_cl_with_suggestion
-                   : diag::err_drv_unknown_argument_with_suggestion;
-      Diags.Report(DiagID) << ArgString << Nearest;
+      ContainsError |= Diags.getDiagnosticLevel(DiagID, SourceLocation()) >
+                       DiagnosticsEngine::Warning;
     }
-    ContainsError |= Diags.getDiagnosticLevel(DiagID, SourceLocation()) >
-                     DiagnosticsEngine::Warning;
   }
 
   for (const Arg *A : Args.filtered(options::OPT_o)) {
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 422375240bab6..0ba29db471617 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4991,15 +4991,17 @@ bool CompilerInvocation::CreateFromArgsImpl(
     Diags.Report(diag::err_drv_missing_argument)
         << Args.getArgString(MissingArgIndex) << MissingArgCount;
 
-  // Issue errors on unknown arguments.
-  for (const auto *A : Args.filtered(OPT_UNKNOWN)) {
-    auto ArgString = A->getAsString(Args);
-    std::string Nearest;
-    if (Opts.findNearest(ArgString, Nearest, VisibilityMask) > 1)
-      Diags.Report(diag::err_drv_unknown_argument) << ArgString;
-    else
-      Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
-          << ArgString << Nearest;
+  if (!Args.hasArg(options::OPT_allow_unrecognized_arguments)) {
+    // Issue errors on unknown arguments.
+    for (const auto *A : Args.filtered(OPT_UNKNOWN)) {
+      auto ArgString = A->getAsString(Args);
+      std::string Nearest;
+      if (Opts.findNearest(ArgString, Nearest, VisibilityMask) > 1)
+        Diags.Report(diag::err_drv_unknown_argument) << ArgString;
+      else
+        Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
+            << ArgString << Nearest;
+    }
   }
 
   ParseFileSystemArgs(Res.getFileSystemOpts(), Args, Diags);
diff --git a/clang/test/Driver/unsupported-option.c b/clang/test/Driver/unsupported-option.c
index 7234e52571582..465f1879b31c8 100644
--- a/clang/test/Driver/unsupported-option.c
+++ b/clang/test/Driver/unsupported-option.c
@@ -32,3 +32,7 @@
 // RUN: not %clang -c -Qunused-arguments --target=aarch64-- -mfpu=crypto-neon-fp-armv8 %s 2>&1 \
 // RUN:   | FileCheck %s --check-prefix=QUNUSED_ARGUMENTS
 // QUNUSED_ARGUMENTS: error: unsupported option '-mfpu=' for target 'aarch64--'
+
+// RUN: %clang %s -invalid --allow-unrecognized-arguments -### 2>&1 | \
+// RUN: FileCheck %s --check-prefix=UNKNOWN_ARGUMENT
+// UNKNOWN_ARGUMENT: warning: argument unused during compilation: '-invalid'

@felix642
Copy link
Contributor Author

felix642 commented Oct 6, 2025

@nicovank @carlosgalvezp @AaronBallman @HerrCai0907 @5chmidti @MaskRay
You were part of the initial PR ( #111453 ) and added comments regarding the initial implementation.
If you have any comments regarding this new patch I would love to hear it.

@AaronBallman
Copy link
Collaborator

This is another attempt at fixing #108455. Please see the initial discussion in the following PR: #111453

Thanks for investigating fixes here!

This parameter is used to suppress the Unknown argument '...' error that clang will emit whenever it encounters an unknown argument.

Personally, I'm not a big fan of having a command line option to control diagnostics of command line options. I think command line options are generally processed in a left-to-right order where the last flag "wins", which is kind of awkward with this design. e.g.,
--allow-unrecognized-arguments --invalid silencing the diagnostic but --invalid --allow-unrecognized-arguments not silencing the diagnostic. Processing the full command line to find --allow-unrecognized-arguments and then continuing to process the command line arguments is possible, but it begs the question of why that's not done for all options related to diagnostic output.

I think this probably should remain an error. Passing unknown inputs to the compiler and expecting to get a known output is a pretty big ask IMO. That said, maybe others have a different opinion.

This is probably an error to make sure the user fixes it's mistake by either removing the argument or renaming it, but there are some cases where it's not possible to fix the issue. For instance, CMake now injects gcc-specific arguments in the clang-tidy command that breaks static-analysis (https://gitlab.kitware.com/cmake/cmake/-/issues/26283)

IMO, that's a CMake bug; adding an option we have to support forever to work around that behavior is not really ideal.

@felix642
Copy link
Contributor Author

felix642 commented Oct 9, 2025

Thank you for the feedback @AaronBallman.

Just to clarify:

IMO, that's a CMake bug; adding an option we have to support forever to work around that behavior is not really ideal.

Undoubtedly, this was only an example on the reasoning behind this change. But I do think it is annoying from a user's perspective to be unable to use clang-tidy / clangd in those situations. They have to either wait for a fix from (in that case cmake) or us to properly fix the issue. This is not the first time this kind of issue happened and this is definitely not going to be the last one.

The main use case I see for this option would be to give the user the ability to run clang-tidy, clangd (or any clang-based tool as a matter of fact) on a gcc project and to get some decents results without having to maintain two separate build commands or to write some convoluted script to transform the gcc-based command line into something that clang will not complain about.
Here's a few examples I found where people struggle with this exact problem:
(https://stackoverflow.com/questions/60227510/clang-tidy-report-error-unknown-argument-when-contain-other-compiler-options)
(https://stackoverflow.com/questions/68178126/clang-tidy-reporting-many-error-unknown-argument)
(clangd/clangd#1939)
(clangd/clangd#662)

If we give the user the ability to run clang-based tools without any changes on they're project would, in my opinion, greatly simplify the integration of those tools in existing projects. It follows the logic of "Make it work and then make it right" and the "Unrecognized argument" is probably one of the only thing keeping them from doing that.

Personally, I'm not a big fan of having a command line option to control diagnostics of command line options.

That being said, I'm wondering if it would be better to instead modify the ClangTool class to be able to ignore those unknown arguments instead of a adding a new argument to the clang-driver. Doing that would allow us to enable this option on a per-tool basis. i.e. A new option could be added to the clang-tidy config file IgnoreUnknownArguments: True to enable this option.

This would limit the scope of this option and would give us the ability to enable it only for the use-cases that we think are needed.

@MaskRay
Copy link
Member

MaskRay commented Oct 10, 2025

You can use the CCC_OVERRIDE_OPTIONS environment variable to remove options.

If we decide to add this functionality, we could consider implementing --start-ignore-unknown-arguments / --end-ignore-unknown-arguments flags. This would be similar to the existing --start-no-unused-arguments / --end-no-unused-arguments flags, which are used to suppress warnings. @mstorsjo

clang --start-no-unused-arguments --rtlib=compiler-rt --unwindlib=libunwind -stdlib=libstdc++ -static-libstdc++ -pthread --end-no-unused-arguments -nostdlib a.o  # no driver warning

Alternative: --unknown-argument=ignore --unknown --unknown-argument=error --unknown --unknown-argument=warning

@cosmicexplorer
Copy link

Referring to command-line flags which clearly specify the standards documents they're referring to in the flag itself as "gcc-specific arguments" seems disingenuous and unprofessional. The reference to Kitware's proposal is the first result from google. The kitware issue clearly describes how to turn these off in the cmake file:

set(CMAKE_CXX_SCAN_FOR_MODULES OFF)

If we give the user the ability to run clang-based tools without any changes on they're project would, in my opinion, greatly simplify the integration of those tools in existing projects.

As I understand it, modules are a pretty fundamental change to the way C++ dependencies work. The clang-tidy docs state that:

Clang compiler errors (such as syntax errors, semantic errors, or other failures that prevent Clang from compiling the code) are reported with the check name clang-diagnostic-error. These represent fundamental compilation failures that must be fixed before clang-tidy can perform its analysis. Unlike other diagnostics, clang-diagnostic-error cannot be disabled, as clang-tidy requires valid code to function.

I also see that clang-tidy has its own preferred flags for module support which do not implement any proposed standard:

  --enable-module-headers-parsing  - Enables preprocessor-level module header parsing
                                     for C++20 and above, empowering specific checks
                                     to detect macro definitions within modules. This
                                     feature may cause performance and parsing issues
                                     and is therefore considered experimental.

I also see that my locally installed copy of clang (version 20.1.8) already supports the exact same functionality, with very slightly misspelled names:

  -fmodule-map-file=<file>
                          Load this module map file
  -fmodules               Enable the 'modules' language feature

The linked issue regarding cmake describes these three arguments as problematic:

error: unknown argument: '-fdeps-format=p1689r5' [clang-diagnostic-error]
error: unknown argument: '-fmodule-mapper=my_exe/test/CMakeFiles/my_exe_lib_tests.dir/Debug/tests.cpp.o.modmap' [clang-diagnostic-error]
error: unknown argument: '-fmodules-ts' [clang-diagnostic-error]

The proposed methodology to directly ignore these inputs seems tantamount to a rejection of the proposals for interoperable module support across build systems.

The main use case I see for this option would be to give the user the ability to run clang-tidy, clangd (or any clang-based tool as a matter of fact) on a gcc project and to get some decents results without having to maintain two separate build commands or to write some convoluted script to transform the gcc-based command line into something that clang will not complain about.

Given that modules are a standard API, and that clang already supports precisely the functionality needed, it's unclear why this is being described as "gcc-based". Is there context that's not being stated here?

the "Unrecognized argument" is probably one of the only thing keeping them from doing that.

Given that my laptop's clang++ version supports very similar command-line arguments already, I'm curious why clang-tidy wouldn't be able to make use of that support from clang. Is there some deeper issue that stops clang-tidy from being able to support C++20 modules, or from using the code in clang that processes module dependencies?

Much like the make jobserver protocol that ninja and cargo now implement, gcc describes a very explicit effort of at least 5 years to get external buy-in for the exact functionality that is being described as "gcc-based" here (https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Module-Mapper.html):

The mapper protocol was published as “A Module Mapper” https://wg21.link/p1184. The implementation is provided by libcody, https://github.com/urnathan/libcody, which specifies the canonical protocol definition. A proof of concept server implementation embedded in make was described in ”Make Me A Module”, https://wg21.link/p1602.

Removing arguments which define dependency relationships from the command line, based upon internal filtering logic that's defined in the specific version of clang-tidy's compiled C++ code and not in the build system, strikes me as a decision which could incur later user requests for an additional flag to cancel out the first flag filtering functionality, if the user ever figures out their dependency issue was because of this filtering logic in the first place.

In particular, many build systems such as bazel will cache process executions based upon the command-line arguments, especially if those arguments specify input files for the compiler to read. It would seem more appropriate to make such incompatibilities visible to the build system by writing diagnostics about unsupported flags to a separate output file, instead of subtly changing the semantics of command-line parsing in a data-dependent manner.

@carlosgalvezp
Copy link
Contributor

FWIW, clangd already supports this feature: https://clangd.llvm.org/config.html#remove

You can use the CCC_OVERRIDE_OPTIONS environment variable to remove options.

This sounds like it could be a solution!

we could consider implementing --start-ignore-unknown-arguments / --end-ignore-unknown-arguments flags

I don't think this would work, because it would need to be added to the GCC compile flags, and then those flags would be unknown to GCC?

Regarding modules, I think it's out of scope for this ticket and deserves an RFC/ticket of its own.

One example use case I've personally come across is as follows:

  • I have code for an STM32 microcontroller.
  • I build it with the GNU GCC ARM toolchain, that passes some specific -m flag that does not exist in clang.
  • clang rejects that flag.
  • I want to run clang-tidy static analysis on the code. How do I do it?

The problem I see with simply removing flags is that the code may not be analysed correctly, which may lead to other errors, which people will then ask us to patch, and so on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ignore unknown flags in clang-tidy
6 participants