[clang] [clang] Report an invalid -mllvm or -mmlir argument instead of exiting (PR #229781)
Gabriele Gristina via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 05:19:12 PDT 2026
https://github.com/matrix updated https://github.com/llvm/llvm-project/pull/229781
>From 110fe0af4d0fd463e7d82202bc95980602598fd3 Mon Sep 17 00:00:00 2001
From: Gabriele Gristina <gabriele.gristina at gmail.com>
Date: Wed, 7 Oct 2026 16:37:45 +0200
Subject: [PATCH] [clang] Report an invalid -mllvm or -mmlir argument instead
of exiting
An option passed through -mllvm reaches llvm::cl::ParseCommandLineOptions,
which prints to llvm::errs() and calls exit() when it is not given a stream
to report to. clang passes nullptr, so an invalid option ends the process
from inside the option parser.
That is fatal for anything that embeds clang as a library, hiprtc among
others: one invalid -mllvm option ends the host process rather than failing
the compilation it belongs to, and a caller cannot test whether an option
exists, because asking is what kills it.
Pass llvm::errs(), the stream the parser falls back to when it is given
none, so the text it writes for an unknown argument is unchanged, and report
the failure it returns as a diagnostic. ExecuteCompilerInvocation already
returns false once the error count has grown, and cc1as already skips the
assembler once a diagnostic has been reported, so the invocation fails the
ordinary way and the caller keeps control.
Three call sites forward options to a cl:: parser this way and all three
pass nullptr, so all three are changed: -mllvm and -mmlir in
ExecuteCompilerInvocation, and -mllvm in cc1as_main.cpp. The diagnostic
names the flag the option came from. The -mmlir one is compiled only when
CLANG_ENABLE_CIR is on, so clang/test/CIR/mlirargs.c covers it only in that
configuration.
---
clang/docs/ReleaseNotes.md | 2 ++
.../clang/Basic/DiagnosticFrontendKinds.td | 2 ++
.../ExecuteCompilerInvocation.cpp | 21 +++++++++++++------
clang/test/CIR/mlirargs.c | 1 +
clang/test/Driver/unknown-arg.c | 4 ++++
clang/test/Frontend/unknown-arg.c | 7 +++++++
clang/tools/driver/cc1as_main.cpp | 8 +++++--
7 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index efbcc198a28e3..dd64034413cf1 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -882,6 +882,8 @@ features cannot lower the translation-unit ABI level;
#### Miscellaneous Bug Fixes
+- Fixed clang terminating the process from inside the LLVM or MLIR option parser, instead of reporting a diagnostic, when an invalid option was passed through `-mllvm` or `-mmlir`.
+
#### Miscellaneous Clang Crashes Fixed
- Fixed a crash in CTAD for type alias templates when the aggregate deduction guide could not be resolved. (#GH206994)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 232475b6fb5a7..0ab712b4e9374 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -153,6 +153,8 @@ def err_cir_input_missing_triple : Error<
"missing target triple in ClangIR input">;
def err_cir_input_triple_mismatch : Error<
"target triple '%0' of ClangIR input does not match the target '%1'">;
+def err_fe_invalid_forwarded_option : Error<
+ "invalid argument in '%select{-mllvm|-mmlir}0'">;
def err_fe_invalid_multiple_actions : Error<
"'%0' action ignored; '%1' action specified previously">;
def err_fe_invalid_alignment : Error<
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index e872f0823f23e..5077115c2e878 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -34,6 +34,7 @@
#include "llvm/Support/BuryPointer.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
#if CLANG_ENABLE_CIR
#include "mlir/IR/AsmState.h"
@@ -264,9 +265,13 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) {
for (unsigned i = 0; i != NumArgs; ++i)
Args[i + 1] = Clang->getFrontendOpts().LLVMArgs[i].c_str();
Args[NumArgs + 1] = nullptr;
- llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"",
- /*Errs=*/nullptr,
- /*VFS=*/&Clang->getVirtualFileSystem());
+ // With no stream to report to, the parser calls exit(), which ends the
+ // process that embeds clang. Keep its output where it already went.
+ if (!llvm::cl::ParseCommandLineOptions(
+ NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(),
+ /*VFS=*/&Clang->getVirtualFileSystem()))
+ Clang->getDiagnostics().Report(diag::err_fe_invalid_forwarded_option)
+ << 0;
}
#if CLANG_ENABLE_STATIC_ANALYZER
@@ -313,9 +318,13 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) {
for (unsigned i = 0; i != NumArgs; ++i)
Args[i + 1] = Clang->getFrontendOpts().MLIRArgs[i].c_str();
Args[NumArgs + 1] = nullptr;
- llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(),
- /*Description=*/"", /*Errs=*/nullptr,
- &Clang->getVirtualFileSystem());
+ // With no stream to report to, the parser calls exit(), which ends the
+ // process that embeds clang. Keep its output where it already went.
+ if (!llvm::cl::ParseCommandLineOptions(
+ NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(),
+ /*VFS=*/&Clang->getVirtualFileSystem()))
+ Clang->getDiagnostics().Report(diag::err_fe_invalid_forwarded_option)
+ << 1;
}
#endif
diff --git a/clang/test/CIR/mlirargs.c b/clang/test/CIR/mlirargs.c
index cfb07197ef185..b28c7c59e3797 100644
--- a/clang/test/CIR/mlirargs.c
+++ b/clang/test/CIR/mlirargs.c
@@ -8,5 +8,6 @@
// WRONG: clang (MLIR option parsing): Did you mean '--mlir-disable-threading'?
// WRONG: clang (MLIR option parsing): Unknown command line argument '-mlir-print-op-genericd'. Try: 'clang (MLIR option parsing) --help'
// WRONG: clang (MLIR option parsing): Did you mean '--mlir-print-op-generic'?
+// WRONG: error: invalid argument in '-mmlir'
// CC1: "-mmlir" "-mlir-disable-threading"
diff --git a/clang/test/Driver/unknown-arg.c b/clang/test/Driver/unknown-arg.c
index 26032a407f413..9bddef95d5fa0 100644
--- a/clang/test/Driver/unknown-arg.c
+++ b/clang/test/Driver/unknown-arg.c
@@ -20,6 +20,8 @@
// RUN: FileCheck %s --check-prefix=SILENT
// RUN: not %clang -cc1as -hell --version 2>&1 | \
// RUN: FileCheck %s --check-prefix=CC1AS-DID-YOU-MEAN
+// RUN: not %clang -cc1as -mllvm -helium 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CC1AS-MLLVM
// RUN: not %clang -cc1asphalt -help 2>&1 | \
// RUN: FileCheck %s --check-prefix=UNKNOWN-INTEGRATED
@@ -59,6 +61,8 @@
// SILENT-NOT: warning:
// CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'?
// CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'?
+// CC1AS-MLLVM: clang (LLVM option parsing): Unknown command line argument '-helium'
+// CC1AS-MLLVM: error: invalid argument in '-mllvm'
// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1', '-cc1as' and '-cc1gen-reproducer'.
// RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s
diff --git a/clang/test/Frontend/unknown-arg.c b/clang/test/Frontend/unknown-arg.c
index 74a6c842da908..9cebe195c2413 100644
--- a/clang/test/Frontend/unknown-arg.c
+++ b/clang/test/Frontend/unknown-arg.c
@@ -6,7 +6,14 @@
// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN
// RUN: not %clang_cc1 --version 2>&1 | \
// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN-VER
+// RUN: not %clang_cc1 %s -E -mllvm -helium 2>&1 | \
+// RUN: FileCheck %s --check-prefix=MLLVM
// CHECK: error: unknown argument: '--helium'
// DID-YOU-MEAN: error: unknown argument '--hel['; did you mean '--help'?
// DID-YOU-MEAN-VER: error: unknown argument '--version'; did you mean '-version'?
+
+// An unknown option behind -mllvm is reported, and the text LLVM's own parser
+// writes is unchanged, because it still goes to the stream it used before.
+// MLLVM: clang (LLVM option parsing): Unknown command line argument '-helium'
+// MLLVM: error: invalid argument in '-mllvm'
diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp
index 077cd69ce4e2c..41f1562208a5d 100644
--- a/clang/tools/driver/cc1as_main.cpp
+++ b/clang/tools/driver/cc1as_main.cpp
@@ -731,8 +731,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
for (unsigned i = 0; i != NumArgs; ++i)
Args[i + 1] = Asm.LLVMArgs[i].c_str();
Args[NumArgs + 1] = nullptr;
- llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"",
- /*Errs=*/nullptr, /*VFS=*/VFS.get());
+ // With no stream to report to, the parser calls exit(), which ends the
+ // process that embeds clang. Keep its output where it already went.
+ if (!llvm::cl::ParseCommandLineOptions(
+ NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(),
+ /*VFS=*/VFS.get()))
+ Diags.Report(diag::err_fe_invalid_forwarded_option) << 0;
}
// Execute the invocation, unless there were parsing errors.
More information about the cfe-commits
mailing list