[clang-tools-extra] [clang-tidy] Fix handling --driver-mode= (PR #66553)

Piotr Zegar via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 16 01:03:42 PDT 2023


https://github.com/PiotrZSL updated https://github.com/llvm/llvm-project/pull/66553

>From d67dbfb1e926e90b2f4a067c86732bd41783791d Mon Sep 17 00:00:00 2001
From: Piotr Zegar <me at piotrzegar.pl>
Date: Fri, 15 Sep 2023 21:39:17 +0000
Subject: [PATCH] [clang-tidy] Fix handling --driver-mode=

Driver mode passed as an extra argument (command line or config)
were not utilized for removing invalid arguments in
stripPositionalArgs function, and even if passed as config
driver mode were not used for dependency file striping
leading to invalid handling of -MD. Additionally driver mode
were needed even if user already added cl.exe after --.
---
 .../clang-tidy/tool/ClangTidyMain.cpp         | 96 ++++++++++++++++++-
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 +
 .../clang-tidy/infrastructure/driver-mode.cpp | 57 +++++++++++
 3 files changed, 157 insertions(+), 1 deletion(-)
 create mode 100644 clang-tools-extra/test/clang-tidy/infrastructure/driver-mode.cpp

diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 0d2593e74f052b9..d922abeb3ba9bca 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -19,6 +19,7 @@
 #include "../ClangTidyForceLinker.h"
 #include "../GlobList.h"
 #include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/PluginLoader.h"
@@ -26,7 +27,11 @@
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/WithColor.h"
+#include <algorithm>
+#include <iterator>
 #include <optional>
+#include <string>
+#include <vector>
 
 using namespace clang::tooling;
 using namespace llvm;
@@ -450,7 +455,7 @@ static StringRef closest(StringRef Value, const StringSet<> &Allowed) {
   return Closest;
 }
 
-static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";
+static constexpr StringRef VerifyConfigWarningEnd = " [-verify-config]\n";
 
 static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,
                          StringRef Source) {
@@ -550,9 +555,91 @@ static llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> createBaseFS() {
   return BaseFS;
 }
 
+static llvm::Expected<CommonOptionsParser>
+recreateOptionsParserIfNeeded(llvm::ArrayRef<const char *> Args,
+                              llvm::Expected<CommonOptionsParser> OptionsParser,
+                              const ClangTidyOptions &EffectiveOptions) {
+
+  auto DoubleDashIt = std::find(Args.begin(), Args.end(), StringRef("--"));
+
+  // Exit if we don't have any compiler arguments
+  if (DoubleDashIt == Args.end() || Args.empty() ||
+      Args.back() == StringRef("--"))
+    return OptionsParser;
+
+  auto IsDriverMode = [](StringRef Argument) {
+    return Argument.startswith("--driver-mode=");
+  };
+
+  // Exit if --driver-mode= is explicitly passed in compiler arguments
+  if (Args.end() !=
+      std::find_if(std::next(DoubleDashIt), Args.end(), IsDriverMode))
+    return OptionsParser;
+
+  std::vector<std::string> CommandArguments(std::next(DoubleDashIt),
+                                            Args.end());
+
+  // Add clang-tool as program name if not added
+  if (CommandArguments.empty() ||
+      llvm::StringRef(CommandArguments.front()).startswith("-"))
+    CommandArguments.insert(CommandArguments.begin(), "clang-tool");
+
+  // Apply --extra-arg and --extra-arg-before to compiler arguments
+  CommandArguments =
+      OptionsParser->getArgumentsAdjuster()(CommandArguments, "");
+
+  // Apply ExtraArgsBefore from clang-tidy config to compiler arguments
+  if (EffectiveOptions.ExtraArgsBefore)
+    CommandArguments = tooling::getInsertArgumentAdjuster(
+        *EffectiveOptions.ExtraArgsBefore,
+        tooling::ArgumentInsertPosition::BEGIN)(CommandArguments, "");
+
+  // Apply ExtraArgs from clang-tidy config to compiler arguments
+  if (EffectiveOptions.ExtraArgs)
+    CommandArguments = tooling::getInsertArgumentAdjuster(
+        *EffectiveOptions.ExtraArgs,
+        tooling::ArgumentInsertPosition::END)(CommandArguments, "");
+
+  // Check if now we have --driver-mode=
+  auto DriverModeIt = std::find_if(CommandArguments.begin(),
+                                   CommandArguments.end(), IsDriverMode);
+  if (DriverModeIt == CommandArguments.end()) {
+    // Try to detect and add --driver-mode=
+    std::string ExeName = CommandArguments.front();
+    tooling::addTargetAndModeForProgramName(CommandArguments, ExeName);
+    DriverModeIt = std::find_if(CommandArguments.begin(),
+                                CommandArguments.end(), IsDriverMode);
+  }
+
+  // Exit if there is no --driver-mode= at this stage
+  if (DriverModeIt == CommandArguments.end())
+    return OptionsParser;
+
+  std::vector<const char *> NewArgs(Args.begin(), Args.end());
+
+  // Find place to insert --driver-mode= into new args, best after program name.
+  auto InsertIt =
+      NewArgs.begin() + std::distance(Args.begin(), DoubleDashIt) + 1U;
+  if (!StringRef(*InsertIt).startswith("-"))
+    ++InsertIt;
+  NewArgs.insert(InsertIt, DriverModeIt->c_str());
+
+  // Re-create CommonOptionsParser with assumption that
+  // FixedCompilationDatabase::loadFromCommandLine will be now called with
+  // proper --driver-mode=
+  int ArgC = NewArgs.size();
+  const char **ArgV = NewArgs.data();
+  return CommonOptionsParser::create(ArgC, ArgV, ClangTidyCategory,
+                                     cl::ZeroOrMore);
+}
+
 int clangTidyMain(int argc, const char **argv) {
   llvm::InitLLVM X(argc, argv);
 
+  // Save original arguments because CommonOptionsParser::create will change
+  // `argc`.
+  llvm::ArrayRef<const char *> Args(argv, argc);
+
   // Enable help for -load option, if plugins are enabled.
   if (cl::Option *LoadOpt = cl::getRegisteredOptions().lookup("load"))
     LoadOpt->addCategory(ClangTidyCategory);
@@ -585,6 +672,13 @@ int clangTidyMain(int argc, const char **argv) {
   SmallString<256> FilePath = makeAbsolute(FileName);
   ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
 
+  OptionsParser = recreateOptionsParserIfNeeded(Args, std::move(OptionsParser),
+                                                EffectiveOptions);
+  if (!OptionsParser) {
+    llvm::WithColor::error() << llvm::toString(OptionsParser.takeError());
+    return 1;
+  }
+
   std::vector<std::string> EnabledChecks =
       getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
 
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6d6f51998a01e57..7580f0160ce9b12 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -122,6 +122,11 @@ Improvements to clang-tidy
   if any :program:`clang-tidy` subprocess exits with a non-zero code or if
   exporting fixes fails.
 
+- Improved handling of `--driver-mode=`, now automatically deducing it from
+  the compiler name after `--`, or properly utilizing it when passed as an
+  extra argument during :program:`clang-tidy` invocation with explicit compiler
+  arguments.
+
 New checks
 ^^^^^^^^^^
 
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/driver-mode.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/driver-mode.cpp
new file mode 100644
index 000000000000000..fed017e5de09f23
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/driver-mode.cpp
@@ -0,0 +1,57 @@
+// REQUIRES: shell
+// RUN: rm -rf "%t"
+// RUN: mkdir "%t"
+// RUN: cp "%s" "%t/code.cpp"
+// RUN: echo '' > "%t/.clang-tidy"
+
+// Compile commands tests (explicit --driver-mode):
+// RUN: echo '[{"directory":"%t/","file":"code.cpp","command":"dummy-compiler /W4 code.cpp"}]' > "%t/compile_commands.json"
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --extra-arg=--driver-mode=cl 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --extra-arg-before=--driver-mode=cl 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --config='{ExtraArgs: ["--driver-mode=cl"]}' 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --config='{ExtraArgsBefore: ["--driver-mode=cl"]}' 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// Compile commands tests (implicit --driver-mode):
+// RUN: echo '[{"directory":"%t/","file":"code.cpp","command":"cl.exe /W4 code.cpp"}]' > "%t/compile_commands.json"
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// Compile commands tests (negative)
+// RUN: echo '[{"directory":"%t/","file":"code.cpp","command":"dummy-compiler -MT /W4 code.cpp"}]' > "%t/compile_commands.json"
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' --check-prefix=NEGATIVE_CHECK --allow-empty %s
+
+// Command line tests (explicit --driver-mode):
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" "%t/code.cpp" \
+// RUN:   -- --driver-mode=cl -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" "%t/code.cpp" \
+// RUN:   --extra-arg=--driver-mode=cl -- -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" "%t/code.cpp" \
+// RUN:   --extra-arg-before=--driver-mode=cl -- -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --config='{ExtraArgs: ["--driver-mode=cl"]}' -- -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   --config='{ExtraArgsBefore: ["--driver-mode=cl"]}' -- -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// Command line tests (implicit --driver-mode):
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   -- cl.exe -MD /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// Command line tests (negative)
+// RUN: clang-tidy --checks="-*,clang-diagnostic-reorder-ctor,readability-redundant-string-cstr" -p="%t" "%t/code.cpp" \
+// RUN:   -- dummy-compiler -MT /W4 code.cpp 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' --check-prefix=NEGATIVE_CHECK --allow-empty %s
+
+struct string {};
+
+struct A {
+    A(string aa, string bb) : b(bb), a(aa) {}
+// CHECK: code.cpp:[[@LINE-1]]:31: warning: field 'b' will be initialized after field 'a' [clang-diagnostic-reorder-ctor]
+// NEGATIVE_CHECK-NOT: warning: field 'b' will be initialized after field 'a' [clang-diagnostic-reorder-ctor]
+
+    string a;
+    string b;
+};



More information about the cfe-commits mailing list