https://github.com/thorsten-klein created 
https://github.com/llvm/llvm-project/pull/176940

new option `HeaderDirs` for `llvm-header-guard` is added.

Defaults to `include`. When checking header guards, the header file path is
searched for the first matching directory in this list. All parent path
components preceding that directory are discarded, so the generated header
  guard starts at the matched include directory.

Ref: #71732

>From 54fe622d73bf90007dfcb7342f720d1390026e7d Mon Sep 17 00:00:00 2001
From: Thorsten Klein <[email protected]>
Date: Mon, 19 Jan 2026 15:43:14 +0100
Subject: [PATCH] added option `llvm-header-guard.HeaderDirs`

new option HeaderDirs for llvm-header-guard is added.

Defaults to `include`. When checking header guards, the header file path
is
searched for the first matching directory in this list. All parent
path
components preceding that directory are discarded, so the generated
header
  guard starts at the matched include directory.
---
 .../clang-tidy/llvm/HeaderGuardCheck.cpp      |  23 +++-
 .../clang-tidy/llvm/HeaderGuardCheck.h        |   4 +
 clang-tools-extra/docs/ReleaseNotes.rst       |   8 ++
 .../clang-tidy/checks/llvm/header-guard.rst   |   8 ++
 .../clang-tidy/checkers/llvm/header-guard.cpp | 104 ++++++++++++++++++
 .../llvm/header-guard/include/correct.hpp     |   6 +
 .../llvm/header-guard/include/missing.hpp     |   0
 .../llvm/header-guard/include/wrong.hpp       |   6 +
 .../llvm/header-guard/other/correct.hpp       |  11 ++
 .../llvm/header-guard/other/missing.hpp       |   0
 .../llvm/header-guard/other/wrong.hpp         |   6 +
 11 files changed, 171 insertions(+), 5 deletions(-)
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/correct.hpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/missing.hpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/wrong.hpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/correct.hpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/missing.hpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/wrong.hpp

diff --git a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
index ef8b6b1dfb8f7..36e837e203a89 100644
--- a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "HeaderGuardCheck.h"
+#include "../utils/OptionsUtils.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/Path.h"
 
@@ -14,7 +15,9 @@ namespace clang::tidy::llvm_check {
 
 LLVMHeaderGuardCheck::LLVMHeaderGuardCheck(StringRef Name,
                                            ClangTidyContext *Context)
-    : HeaderGuardCheck(Name, Context) {}
+    : HeaderGuardCheck(Name, Context),
+      HeaderDirs(utils::options::parseStringList(
+          Options.get("HeaderDirs", "include"))) {}
 
 std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename,
                                                  StringRef OldGuard) {
@@ -27,10 +30,15 @@ std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef 
Filename,
   // Sanitize the path. There are some rules for compatibility with the 
historic
   // style in include/llvm and include/clang which we want to preserve.
 
-  // We don't want _INCLUDE_ in our guards.
-  const size_t PosInclude = Guard.rfind("include/");
-  if (PosInclude != StringRef::npos)
-    Guard = Guard.substr(PosInclude + std::strlen("include/"));
+  // consider all directories from HeaderDirs option. Stop at first found.
+  for (StringRef HeaderDir : HeaderDirs) {
+    size_t PosHeaderDir = Guard.rfind(HeaderDir.str() + "/");
+    if (PosHeaderDir != StringRef::npos) {
+      // We don't want the header dir in our guards, i.e. _INCLUDE_
+      Guard = Guard.substr(PosHeaderDir + HeaderDir.size() + 1);
+      break; // stop at first found
+    }
+  }
 
   // For clang we drop the _TOOLS_.
   const size_t PosToolsClang = Guard.rfind("tools/clang/");
@@ -64,4 +72,9 @@ std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef 
Filename,
   return StringRef(Guard).upper();
 }
 
+void LLVMHeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HeaderDirs",
+                utils::options::serializeStringList(HeaderDirs));
+}
+
 } // namespace clang::tidy::llvm_check
diff --git a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h 
b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
index cfc920bb85e23..a7447a5c8863d 100644
--- a/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
+++ b/clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
@@ -21,7 +21,11 @@ class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
   LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context);
 
   bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
   std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+
+private:
+  const std::vector<StringRef> HeaderDirs;
 };
 
 } // namespace clang::tidy::llvm_check
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 94a11b1acb73a..61889d8b90210 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -113,6 +113,14 @@ Changes in existing checks
   <clang-tidy/checks/performance/move-const-arg>` check by avoiding false
   positives on trivially copyable types with a non-public copy constructor.
 
+- Improved :doc:`llvm-header-guard
+  <clang-tidy/checks/llvm/header-guard>` check by adding the option
+  `HeaderDirs` which is a list of one or more include directory names.
+  Defaults to `include`. When checking header guards, the header file path is
+  searched for the first matching directory in this list. All parent path
+  components preceding that directory are discarded, so the generated header
+  guard starts at the matched include directory.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst 
b/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
index 74eb10ba6fcf9..f65bea01421f0 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
@@ -4,3 +4,11 @@ llvm-header-guard
 =================
 
 Finds and fixes header guards that do not adhere to LLVM style.
+
+Options
+-------
+
+.. option:: HeaderDirs
+
+   A list of directories where the include guard string will start.
+   defaults to `include`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard.cpp
new file mode 100644
index 0000000000000..8cc3ea8f26581
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard.cpp
@@ -0,0 +1,104 @@
+#include "header-guard/include/correct.hpp"
+#include "header-guard/include/missing.hpp"
+#include "header-guard/include/wrong.hpp"
+
+#include "header-guard/other/correct.hpp"
+#include "header-guard/other/missing.hpp"
+#include "header-guard/other/wrong.hpp"
+
+// ---------------------------------------
+// TEST 1: Use no config options (default)
+// ---------------------------------------
+// RUN: %check_clang_tidy %s llvm-header-guard %t -export-fixes=%t.1.yaml 
--header-filter=.* -- -I%S > %t.1.msg 2>&1
+// RUN: FileCheck -input-file=%t.1.msg -check-prefix=CHECK-MESSAGES1 %s
+// RUN: FileCheck -input-file=%t.1.yaml -check-prefix=CHECK-YAML1 %s
+
+// CHECK-MESSAGES1: header-guard/include/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES1: header-guard/other/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES1: header-guard/include/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+// CHECK-MESSAGES1: header-guard/other/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+
+// CHECK-YAML1: Message:         header is missing header guard
+// CHECK-YAML1: FilePath:        '{{.*}}/header-guard/include/missing.hpp'
+// CHECK-YAML1: ReplacementText: "#ifndef MISSING_HPP\n#define 
MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML1: Message:         header guard does not follow preferred style
+// CHECK-YAML1: FilePath:        '{{.*}}/header-guard/include/wrong.hpp'
+// CHECK-YAML1: ReplacementText: WRONG_HPP
+
+// CHECK-YAML1: Message:         header is missing header guard
+// CHECK-YAML1: FilePath:        '{{.*}}/header-guard/other/missing.hpp'
+// CHECK-YAML1: ReplacementText: "#ifndef 
LLVM_HEADER_GUARD_OTHER_MISSING_HPP\n#define 
LLVM_HEADER_GUARD_OTHER_MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML1: Message:         header guard does not follow preferred style
+// CHECK-YAML1: FilePath:        '{{.*}}/header-guard/other/wrong.hpp'
+// CHECK-YAML1: ReplacementText: LLVM_HEADER_GUARD_OTHER_WRONG_HPP
+
+// ---------------------------------------
+// TEST 2: Set option HeaderDirs=other
+// ---------------------------------------
+// RUN: %check_clang_tidy %s llvm-header-guard %t -export-fixes=%t.2.yaml 
--header-filter=.* \
+// RUN:   --config='{CheckOptions: { \
+// RUN:     llvm-header-guard.HeaderDirs: other, \
+// RUN:   }}' -- -I%S > %t.2.msg 2>&1
+// RUN: FileCheck -input-file=%t.2.msg -check-prefix=CHECK-MESSAGES2 %s
+// RUN: FileCheck -input-file=%t.2.yaml -check-prefix=CHECK-YAML2 %s
+
+// CHECK-MESSAGES2: header-guard/include/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES2: header-guard/other/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES2: header-guard/include/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+// CHECK-MESSAGES2: header-guard/other/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+
+// CHECK-YAML2: Message:         header guard does not follow preferred style
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/include/correct.hpp'
+// CHECK-YAML2: ReplacementText: LLVM_HEADER_GUARD_INCLUDE_CORRECT_HPP
+// CHECK-YAML2: Message:         header is missing header guard
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/include/missing.hpp'
+// CHECK-YAML2: ReplacementText: "#ifndef 
LLVM_HEADER_GUARD_INCLUDE_MISSING_HPP\n#define 
LLVM_HEADER_GUARD_INCLUDE_MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML2: Message:         header guard does not follow preferred style
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/include/wrong.hpp'
+// CHECK-YAML2: ReplacementText: LLVM_HEADER_GUARD_INCLUDE_WRONG_HPP
+
+// CHECK-YAML2: Message:         header guard does not follow preferred style
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/other/correct.hpp'
+// CHECK-YAML2: ReplacementText: CORRECT_HPP
+// CHECK-YAML2: Message:         header is missing header guard
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/other/missing.hpp'
+// CHECK-YAML2: ReplacementText: "#ifndef MISSING_HPP\n#define 
MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML2: Message:         header guard does not follow preferred style
+// CHECK-YAML2: FilePath:        '{{.*}}/header-guard/other/wrong.hpp'
+// CHECK-YAML2: ReplacementText: WRONG_HPP
+
+
+// ---------------------------------------
+// TEST 3: Set option HeaderDirs=include;other
+// ---------------------------------------
+// RUN: %check_clang_tidy %s llvm-header-guard %t -export-fixes=%t.3.yaml 
--header-filter=.* \
+// RUN:   --config='{CheckOptions: { \
+// RUN:     llvm-header-guard.HeaderDirs: include;other, \
+// RUN:   }}' -- -I%S > %t.3.msg 2>&1
+// RUN: FileCheck -input-file=%t.3.msg -check-prefix=CHECK-MESSAGES3 %s
+// RUN: FileCheck -input-file=%t.3.yaml -check-prefix=CHECK-YAML3 %s
+
+// CHECK-MESSAGES3: header-guard/include/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES3: header-guard/other/missing.hpp:1:1: warning: header is 
missing header guard [llvm-header-guard]
+// CHECK-MESSAGES3: header-guard/include/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+// CHECK-MESSAGES3: header-guard/other/wrong.hpp:1:9: warning: header guard 
does not follow preferred style [llvm-header-guard]
+
+// CHECK-YAML3: Message:         header is missing header guard
+// CHECK-YAML3: FilePath:        '{{.*}}/header-guard/include/missing.hpp'
+// CHECK-YAML3: ReplacementText: "#ifndef MISSING_HPP\n#define 
MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML3: Message:         header guard does not follow preferred style
+// CHECK-YAML3: FilePath:        '{{.*}}/header-guard/include/wrong.hpp'
+// CHECK-YAML3: ReplacementText: WRONG_HPP
+
+// CHECK-YAML3: Message:         header guard does not follow preferred style
+// CHECK-YAML3: FilePath:        '{{.*}}/header-guard/other/correct.hpp'
+// CHECK-YAML3: ReplacementText: CORRECT_HPP
+// CHECK-YAML3: Message:         header is missing header guard
+// CHECK-YAML3: FilePath:        '{{.*}}/header-guard/other/missing.hpp'
+// CHECK-YAML3: ReplacementText: "#ifndef MISSING_HPP\n#define 
MISSING_HPP\n\n\n#endif\n"
+// CHECK-YAML3: Message:         header guard does not follow preferred style
+// CHECK-YAML3: FilePath:        '{{.*}}/header-guard/other/wrong.hpp'
+// CHECK-YAML3: ReplacementText: WRONG_HPP
+
+
+
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/correct.hpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/correct.hpp
new file mode 100644
index 0000000000000..19f3347459a7a
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/correct.hpp
@@ -0,0 +1,6 @@
+#ifndef CORRECT_HPP
+#define CORRECT_HPP
+
+// do anything
+
+#endif
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/missing.hpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/missing.hpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/wrong.hpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/wrong.hpp
new file mode 100644
index 0000000000000..bc58b550aca09
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/include/wrong.hpp
@@ -0,0 +1,6 @@
+#ifndef HERE_IS_SOMETHING_WRONG_HPP
+#define HERE_IS_SOMETHING_WRONG_HPP
+
+// do anything
+
+#endif
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/correct.hpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/correct.hpp
new file mode 100644
index 0000000000000..f3da7919d5853
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/correct.hpp
@@ -0,0 +1,11 @@
+#ifndef LLVM_HEADER_GUARD_OTHER_CORRECT_HPP
+#define LLVM_HEADER_GUARD_OTHER_CORRECT_HPP
+
+#ifndef CORRECT_HPP
+#define CORRECT_HPP
+
+// do anything
+
+#endif
+
+#endif
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/missing.hpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/missing.hpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/wrong.hpp 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/wrong.hpp
new file mode 100644
index 0000000000000..7b8abb25cf951
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/header-guard/other/wrong.hpp
@@ -0,0 +1,6 @@
+#ifndef SOME_WRONG_HEADER_GUARD_HPP
+#define SOME_WRONG_HEADER_GUARD_HPP
+
+// do anything
+
+#endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to