[clang] [clang-cl] Don't count linker inputs against /Fo and /Fa (PR #232611)

Haohai Wen via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 18 20:00:55 PDT 2026


https://github.com/HaohaiWen updated https://github.com/llvm/llvm-project/pull/232611

>From a83667e7400d0d140814774eaad46386b0e8fbe8 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Fri, 16 Oct 2026 13:33:38 +0800
Subject: [PATCH 1/2] [clang-cl] Don't count linker inputs against /Fo and /Fa

The /Fo and /Fa checks reject a file name when there is more than one
input. But the input list also holds object files, libraries, and
linker options such as -Xlinker, which go straight to the linker and
never get an output named by /Fo or /Fa. So

  clang-cl /c foo.c /Fofoo.obj -Xlinker -bar

failed with "cannot specify '/Fofoo.obj' when compiling multiple source
files". MSVC's cl.exe also accepts .obj and .lib inputs next to
/Fo<file>.

Count only the inputs that get compiled.

Fixes #216085.
---
 clang/lib/Driver/Driver.cpp    | 9 +++++++--
 clang/test/Driver/cl-outputs.c | 8 ++++++++
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a9a21175af9c5e..b6304dee0be618 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3386,10 +3386,15 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
                              const InputList &Inputs,
                              ActionList &Actions) const {
 
+  // Linker inputs go straight to the linker, so they should not be counted
+  // toward the compiler outputs that /Fo and /Fa name.
+  unsigned NumCompiledInputs = llvm::count_if(
+      Inputs, [](const InputTy &I) { return I.first != types::TY_Object; });
+
   // Diagnose misuse of /Fo.
   if (Arg *A = Args.getLastArg(options::OPT__SLASH_Fo)) {
     StringRef V = A->getValue();
-    if (Inputs.size() > 1 && !V.empty() &&
+    if (NumCompiledInputs > 1 && !V.empty() &&
         !llvm::sys::path::is_separator(V.back())) {
       // Check whether /Fo tries to name an output file for multiple inputs.
       Diag(clang::diag::err_drv_out_file_argument_with_multiple_sources)
@@ -3401,7 +3406,7 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
   // Diagnose misuse of /Fa.
   if (Arg *A = Args.getLastArg(options::OPT__SLASH_Fa)) {
     StringRef V = A->getValue();
-    if (Inputs.size() > 1 && !V.empty() &&
+    if (NumCompiledInputs > 1 && !V.empty() &&
         !llvm::sys::path::is_separator(V.back())) {
       // Check whether /Fa tries to name an asm file for multiple inputs.
       Diag(clang::diag::err_drv_out_file_argument_with_multiple_sources)
diff --git a/clang/test/Driver/cl-outputs.c b/clang/test/Driver/cl-outputs.c
index c59adc8acb3b6c..b480f628972145 100644
--- a/clang/test/Driver/cl-outputs.c
+++ b/clang/test/Driver/cl-outputs.c
@@ -37,6 +37,11 @@
 // CHECK-MULTIPLESOURCEOK2: "-o" "cl-outputs.obj"
 // CHECK-MULTIPLESOURCEOK2: "-o" "cl-outputs.obj"
 
+// RUN: %clang_cl /c /Foa.obj -Xlinker -foo -### -- %s foo.obj foo.lib 2>&1 | FileCheck -check-prefix=FoLINKERINPUT %s
+// RUN: %clang_cl /Foa.obj -Xlinker -foo -### -- %s foo.lib 2>&1 | FileCheck -check-prefix=FoLINKERINPUT %s
+// FoLINKERINPUT-NOT: error: cannot specify '/Foa.obj' when compiling multiple source files
+// FoLINKERINPUT: "-o" "a.obj"
+
 // RUN: %clang_cl /c /oa -### -- %s 2>&1 | FileCheck -check-prefix=oNAME1 %s
 // oNAME1:  "-o" "a.obj"
 
@@ -243,6 +248,9 @@
 // RUN: %clang_cl /Fa -### -- %s %s 2>&1 | FileCheck -check-prefix=FaMULTIPLESOURCEOK %s
 // FaMULTIPLESOURCEOK: "-o" "cl-outputs.asm"
 // FaMULTIPLESOURCEOK: "-o" "cl-outputs.asm"
+// RUN: %clang_cl /Faa.asm -Xlinker -foo -### -- %s foo.obj 2>&1 | FileCheck -check-prefix=FaLINKERINPUT %s
+// FaLINKERINPUT-NOT: error: cannot specify '/Faa.asm' when compiling multiple source files
+// FaLINKERINPUT: "-o" "a.asm"
 
 // Copy of the same tests above, but with /FAcsu
 // RUN: %clang_cl /FAcsu -### -- %s 2>&1 | FileCheck -check-prefix=FA_CSU %s

>From a942f97f241b36e7b189979d7d92de8cd76e6110 Mon Sep 17 00:00:00 2001
From: Haohai Wen <haohai.wen at intel.com>
Date: Mon, 19 Oct 2026 10:18:21 +0800
Subject: [PATCH 2/2] Move count_if into inside of /Fo /Fa check

---
 clang/lib/Driver/Driver.cpp | 45 ++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 26 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index b6304dee0be618..66cfd64f1f1cef 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3386,32 +3386,25 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args,
                              const InputList &Inputs,
                              ActionList &Actions) const {
 
-  // Linker inputs go straight to the linker, so they should not be counted
-  // toward the compiler outputs that /Fo and /Fa name.
-  unsigned NumCompiledInputs = llvm::count_if(
-      Inputs, [](const InputTy &I) { return I.first != types::TY_Object; });
-
-  // Diagnose misuse of /Fo.
-  if (Arg *A = Args.getLastArg(options::OPT__SLASH_Fo)) {
-    StringRef V = A->getValue();
-    if (NumCompiledInputs > 1 && !V.empty() &&
-        !llvm::sys::path::is_separator(V.back())) {
-      // Check whether /Fo tries to name an output file for multiple inputs.
-      Diag(clang::diag::err_drv_out_file_argument_with_multiple_sources)
-          << A->getSpelling() << V;
-      Args.eraseArg(options::OPT__SLASH_Fo);
-    }
-  }
-
-  // Diagnose misuse of /Fa.
-  if (Arg *A = Args.getLastArg(options::OPT__SLASH_Fa)) {
-    StringRef V = A->getValue();
-    if (NumCompiledInputs > 1 && !V.empty() &&
-        !llvm::sys::path::is_separator(V.back())) {
-      // Check whether /Fa tries to name an asm file for multiple inputs.
-      Diag(clang::diag::err_drv_out_file_argument_with_multiple_sources)
-          << A->getSpelling() << V;
-      Args.eraseArg(options::OPT__SLASH_Fa);
+  // Diagnose misuse of /Fo and /Fa.
+  if (Arg *FoArg = Args.getLastArg(options::OPT__SLASH_Fo),
+      *FaArg = Args.getLastArg(options::OPT__SLASH_Fa);
+      FoArg || FaArg) {
+    // Linker inputs do not produce the outputs named by /Fo and /Fa.
+    unsigned NumCompiledInputs = llvm::count_if(
+        Inputs, [](const InputTy &I) { return I.first != types::TY_Object; });
+
+    for (Arg *A : {FoArg, FaArg}) {
+      if (!A)
+        continue;
+      StringRef V = A->getValue();
+      if (NumCompiledInputs > 1 && !V.empty() &&
+          !llvm::sys::path::is_separator(V.back())) {
+        // Check whether /Fo or /Fa names an output file for multiple inputs.
+        Diag(clang::diag::err_drv_out_file_argument_with_multiple_sources)
+            << A->getSpelling() << V;
+        Args.eraseArg(A->getOption().getID());
+      }
     }
   }
 



More information about the cfe-commits mailing list