KyleFromKitware created this revision.
KyleFromKitware added a reviewer: clang-tools-extra.
KyleFromKitware added a project: clang-tools-extra.
Herald added subscribers: carlosgalvezp, xazax.hun.
Herald added a reviewer: sscalpone.
Herald added a reviewer: njames93.
Herald added a project: All.
KyleFromKitware requested review of this revision.
Herald added a subscriber: cfe-commits.

Having more than one check for header guards can potentially lead
to a conflict where different header guard checks suggest different
corrections. Refactor into a single HeaderGuardCheck that can
create one of multiple HeaderGuardStyle instances from a registry.
Refactor LLVMHeaderGuardCheck to extend this class.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D142673

Files:
  clang-tools-extra/clang-tidy/ClangTidyModule.h
  clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
  clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
  clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
  clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.cpp
  clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.h
  clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
  clang-tools-extra/clang-tidy/readability/CMakeLists.txt
  clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.cpp
  clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.h
  clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tools-extra/clang-tidy/utils/CMakeLists.txt
  clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
  clang-tools-extra/clang-tidy/utils/HeaderGuard.h
  clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.cpp
  clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.h
  clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.cpp
  clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.h
  clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp
@@ -3,6 +3,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/HeaderGuardCheck.h"
+#include "llvm/HeaderGuardStyle.h"
 #include "llvm/IncludeOrderCheck.h"
 #include "gtest/gtest.h"
 #include <optional>
@@ -49,10 +50,18 @@
 }
 
 namespace {
+struct WithEndifCommentStyle : public LLVMHeaderGuardStyle {
+  WithEndifCommentStyle(readability::HeaderGuardCheck *Check)
+      : LLVMHeaderGuardStyle(Check) {}
+  bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
+};
+
 struct WithEndifComment : public LLVMHeaderGuardCheck {
   WithEndifComment(StringRef Name, ClangTidyContext *Context)
       : LLVMHeaderGuardCheck(Name, Context) {}
-  bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
+  std::unique_ptr<utils::HeaderGuardStyle> createHeaderGuardStyle() override {
+    return std::make_unique<WithEndifCommentStyle>(this);
+  }
 };
 
 static std::string
Index: clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.h
@@ -0,0 +1,60 @@
+//===--- MacroHeaderGuardStyle.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MACROHEADERGUARDSTYLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MACROHEADERGUARDSTYLE_H
+
+#include "HeaderGuardStyle.h"
+
+namespace clang::tidy::utils {
+class MacroHeaderGuardStyle : public HeaderGuardStyle {
+public:
+  MacroHeaderGuardStyle(readability::HeaderGuardCheck *Check)
+      : HeaderGuardStyle(Check) {}
+
+  /// Ensure that the provided header guard is a non-reserved identifier.
+  std::string sanitizeHeaderGuard(StringRef Guard);
+
+  /// Returns ``true`` if the check should suggest inserting a trailing comment
+  /// on the ``#endif`` of the header guard. It will use the same name as
+  /// returned by ``HeaderGuardCheck::getHeaderGuard``.
+  virtual bool shouldSuggestEndifComment(StringRef Filename);
+  /// Returns a replacement for the ``#endif`` line with a comment mentioning
+  /// \p HeaderGuard. The replacement should start with ``endif``.
+  virtual std::string formatEndIf(StringRef HeaderGuard);
+  /// Gets the canonical header guard for a file.
+  virtual std::string getHeaderGuard(StringRef Filename,
+                                     StringRef OldGuard = StringRef()) = 0;
+
+  void onHeaderGuard(Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+                     SourceLocation IfndefHash, SourceLocation Ifndef,
+                     SourceLocation IfndefToken, SourceLocation DefineHash,
+                     const Token &Define, SourceLocation EndIfHash,
+                     SourceLocation EndIf) override;
+  void onGuardlessHeader(
+      Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+      SourceLocation StartLoc,
+      const std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>>
+          &Macros) override;
+
+private:
+  bool wouldFixEndifComment(Preprocessor *PP, StringRef FileName,
+                            SourceLocation EndIf, StringRef HeaderGuard,
+                            size_t *EndIfLenPtr = nullptr);
+  std::string
+  checkHeaderGuardDefinition(Preprocessor *PP, SourceLocation Ifndef,
+                             SourceLocation Define, SourceLocation EndIf,
+                             StringRef FileName, StringRef CurHeaderGuard,
+                             std::vector<FixItHint> &FixIts);
+  void checkEndifComment(Preprocessor *PP, StringRef FileName,
+                         SourceLocation EndIf, StringRef HeaderGuard,
+                         std::vector<FixItHint> &FixIts);
+};
+} // namespace clang::tidy::utils
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MACROHEADERGUARDSTYLE_H
Index: clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/utils/MacroHeaderGuardStyle.cpp
@@ -0,0 +1,172 @@
+//===--- MacroHeaderGuardStyle.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MacroHeaderGuardStyle.h"
+#include "../readability/HeaderGuardCheck.h"
+#include "../utils/FileExtensionsUtils.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang::tidy::utils {
+
+void MacroHeaderGuardStyle::onHeaderGuard(
+    Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+    SourceLocation IfndefHash, SourceLocation Ifndef,
+    SourceLocation IfndefToken, SourceLocation DefineHash, const Token &Define,
+    SourceLocation EndIfHash, SourceLocation EndIf) {
+  // If the macro Name is not equal to what we can compute, correct it in
+  // the #ifndef and #define.
+  StringRef CurHeaderGuard = Define.getIdentifierInfo()->getName();
+  std::vector<FixItHint> FixIts;
+  std::string NewGuard =
+      checkHeaderGuardDefinition(PP, IfndefToken, Define.getLocation(), EndIf,
+                                 FileName, CurHeaderGuard, FixIts);
+
+  // Now look at the #endif. We want a comment with the header guard. Fix it
+  // at the slightest deviation.
+  checkEndifComment(PP, FileName, EndIf, NewGuard, FixIts);
+
+  // Bundle all fix-its into one warning. The message depends on whether we
+  // changed the header guard or not.
+  if (!FixIts.empty()) {
+    if (CurHeaderGuard != NewGuard) {
+      Check->diag(Ifndef, "header guard does not follow preferred style")
+          << FixIts;
+    } else {
+      Check->diag(EndIf, "#endif for a header guard should reference the "
+                         "guard macro in a comment")
+          << FixIts;
+    }
+  }
+}
+
+bool MacroHeaderGuardStyle::wouldFixEndifComment(Preprocessor *PP,
+                                                 StringRef FileName,
+                                                 SourceLocation EndIf,
+                                                 StringRef HeaderGuard,
+                                                 size_t *EndIfLenPtr) {
+  if (!EndIf.isValid())
+    return false;
+  const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
+  size_t EndIfLen = std::strcspn(EndIfData, "\r\n");
+  if (EndIfLenPtr)
+    *EndIfLenPtr = EndIfLen;
+
+  StringRef EndIfStr(EndIfData, EndIfLen);
+  EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of("#endif \t"));
+
+  // Give up if there's an escaped newline.
+  size_t FindEscapedNewline = EndIfStr.find_last_not_of(' ');
+  if (FindEscapedNewline != StringRef::npos &&
+      EndIfStr[FindEscapedNewline] == '\\')
+    return false;
+
+  bool IsLineComment =
+      EndIfStr.consume_front("//") ||
+      (EndIfStr.consume_front("/*") && EndIfStr.consume_back("*/"));
+  if (!IsLineComment)
+    return shouldSuggestEndifComment(FileName);
+
+  return EndIfStr.trim() != HeaderGuard;
+}
+
+/// Look for header guards that don't match the preferred style. Emit
+/// fix-its and return the suggested header guard (or the original if no
+/// change was made.
+std::string MacroHeaderGuardStyle::checkHeaderGuardDefinition(
+    Preprocessor *PP, SourceLocation Ifndef, SourceLocation Define,
+    SourceLocation EndIf, StringRef FileName, StringRef CurHeaderGuard,
+    std::vector<FixItHint> &FixIts) {
+  std::string CPPVar = getHeaderGuard(FileName, CurHeaderGuard);
+  CPPVar = sanitizeHeaderGuard(CPPVar);
+  std::string CPPVarUnder = CPPVar + '_';
+
+  // Allow a trailing underscore if and only if we don't have to change the
+  // endif comment too.
+  if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
+      (CurHeaderGuard != CPPVarUnder ||
+       wouldFixEndifComment(PP, FileName, EndIf, CurHeaderGuard))) {
+    FixIts.push_back(FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(
+            Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
+        CPPVar));
+    FixIts.push_back(FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(
+            Define, Define.getLocWithOffset(CurHeaderGuard.size())),
+        CPPVar));
+    return CPPVar;
+  }
+  return std::string(CurHeaderGuard);
+}
+
+/// Checks the comment after the #endif of a header guard and fixes it
+/// if it doesn't match \c HeaderGuard.
+void MacroHeaderGuardStyle::checkEndifComment(Preprocessor *PP,
+                                              StringRef FileName,
+                                              SourceLocation EndIf,
+                                              StringRef HeaderGuard,
+                                              std::vector<FixItHint> &FixIts) {
+  size_t EndIfLen;
+  if (wouldFixEndifComment(PP, FileName, EndIf, HeaderGuard, &EndIfLen)) {
+    FixIts.push_back(FixItHint::CreateReplacement(
+        CharSourceRange::getCharRange(EndIf, EndIf.getLocWithOffset(EndIfLen)),
+        formatEndIf(HeaderGuard)));
+  }
+}
+
+void MacroHeaderGuardStyle::onGuardlessHeader(
+    Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+    SourceLocation StartLoc,
+    const std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>>
+        &Macros) {
+  SourceManager &SM = PP->getSourceManager();
+  std::string CPPVar = getHeaderGuard(FileName);
+  CPPVar = sanitizeHeaderGuard(CPPVar);
+  std::string CPPVarUnder = CPPVar + '_'; // Allow a trailing underscore.
+  // If there's a macro with a name that follows the header guard convention
+  // but was not recognized by the preprocessor as a header guard there must
+  // be code outside of the guarded area. Emit a plain warning without
+  // fix-its.
+  // FIXME: Can we move it into the right spot?
+  bool SeenMacro = false;
+  for (const auto &MacroEntry : Macros) {
+    StringRef Name = std::get<1>(MacroEntry).getIdentifierInfo()->getName();
+    SourceLocation DefineLoc = std::get<1>(MacroEntry).getLocation();
+    if ((Name == CPPVar || Name == CPPVarUnder) &&
+        SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
+      Check->diag(DefineLoc, "code/includes outside of area guarded by "
+                             "header guard; consider moving it");
+      SeenMacro = true;
+      break;
+    }
+  }
+
+  if (SeenMacro)
+    return;
+
+  Check->diag(StartLoc, "header is missing header guard")
+      << FixItHint::CreateInsertion(
+             StartLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + "\n\n")
+      << FixItHint::CreateInsertion(SM.getLocForEndOfFile(SM.translateFile(FE)),
+                                    shouldSuggestEndifComment(FileName)
+                                        ? "\n#" + formatEndIf(CPPVar) + "\n"
+                                        : "\n#endif\n");
+}
+
+std::string MacroHeaderGuardStyle::sanitizeHeaderGuard(StringRef Guard) {
+  // Only reserved identifiers are allowed to start with an '_'.
+  return Guard.drop_while([](char C) { return C == '_'; }).str();
+}
+
+bool MacroHeaderGuardStyle::shouldSuggestEndifComment(StringRef FileName) {
+  return utils::isFileExtension(FileName, Check->getHeaderFileExtensions());
+}
+
+std::string MacroHeaderGuardStyle::formatEndIf(StringRef HeaderGuard) {
+  return "endif // " + HeaderGuard.str();
+}
+} // namespace clang::tidy::utils
Index: clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.h
@@ -0,0 +1,129 @@
+//===--- HeaderGuardStyle.h - clang-tidy ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARDSTYLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARDSTYLE_H
+
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+class MacroInfo;
+
+namespace tidy {
+namespace readability {
+class HeaderGuardCheck;
+} // namespace readability
+
+namespace utils {
+class HeaderGuardStyle {
+public:
+  HeaderGuardStyle(readability::HeaderGuardCheck *Check) : Check(Check) {}
+
+  /// Returns ``true`` if the check should suggest fixing a header file if it
+  /// has an existing header guard.
+  virtual bool shouldFixIfHeaderGuard(StringRef Filename);
+
+  /// Returns ``true`` if the check should suggest fixing a header file if it
+  /// does not have an existing header guard.
+  virtual bool shouldFixIfNoHeaderGuard(StringRef Filename);
+
+  /// @brief Called when a header guard is detected.
+  /// @param PP Preprocessor.
+  /// @param FileName Name of the file.
+  /// @param FE FileEntry.
+  /// @param IfndefHash Location of '#' in '#ifndef'.
+  /// @param Ifndef Location of 'ifndef' in '#ifndef'.
+  /// @param IfndefToken Location of macro token in '#ifndef'.
+  /// @param DefineHash Location of '#' in '#define'.
+  /// @param Define Location of macro token in '#define'.
+  /// @param EndIfHash Location of '#' in '#endif'.
+  /// @param EndIf Location of 'endif' in '#endif'.
+  virtual void onHeaderGuard(Preprocessor *PP, StringRef FileName,
+                             const FileEntry *FE, SourceLocation IfndefHash,
+                             SourceLocation Ifndef, SourceLocation IfndefToken,
+                             SourceLocation DefineHash, const Token &Define,
+                             SourceLocation EndIfHash,
+                             SourceLocation EndIf) = 0;
+
+  /// @brief Called when a header with no header guard is detected.
+  /// @param PP Preprocessor.
+  /// @param FileName Name of the file.
+  /// @param FE FileEntry.
+  /// @param StartLoc Location of the start of the file.
+  /// @param Macros List of macros in this file. Contains location of '#', macro
+  /// token, and macro info.
+  virtual void onGuardlessHeader(
+      Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+      SourceLocation StartLoc,
+      const std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>>
+          &Macros) = 0;
+
+protected:
+  readability::HeaderGuardCheck *Check;
+};
+
+/// A collection of \c HeaderGuardStyle factories.
+///
+/// All clang-tidy modules register their header guard style factories with an
+/// instance of this object.
+class HeaderGuardStyleFactories {
+public:
+  using StyleFactory = std::function<std::unique_ptr<HeaderGuardStyle>(
+      readability::HeaderGuardCheck *Check)>;
+
+  /// Registers style \p Factory with name \p Name.
+  ///
+  /// For all style that have constructors that take a HeaderGuardCheck*, use \c
+  /// registerStyle.
+  void registerStyleFactory(llvm::StringRef Name, StyleFactory Factory);
+
+  /// Registers the \c StyleType with the name \p Name.
+  ///
+  /// This method should be used for all \c HeaderGuardStyles whose constructors
+  /// take one \c HeaderGuardCheck * parameter.
+  ///
+  /// For example, if you have a header guard style like:
+  /// \code
+  /// class MyStyle : public HeaderGuardStyle {
+  ///   bool shouldFixIfHeaderGuard(StringRef Filename) override {
+  ///     ..
+  ///   }
+  /// };
+  /// \endcode
+  /// you can register it with:
+  /// \code
+  /// class MyModule : public ClangTidyModule {
+  ///   void addHeaderGuardStyleFactories(HeaderGuardStyleFactories &Factories)
+  ///   override {
+  ///     Factories.registerStyle<MyStyle>("myproject-my-style");
+  ///   }
+  /// };
+  /// \endcode
+  template <typename StyleType> void registerStyle(llvm::StringRef StyleName) {
+    registerStyleFactory(StyleName, [](readability::HeaderGuardCheck *Check) {
+      return std::make_unique<StyleType>(Check);
+    });
+  }
+
+  typedef llvm::StringMap<StyleFactory> FactoryMap;
+  FactoryMap::const_iterator begin() const { return Factories.begin(); }
+  FactoryMap::const_iterator end() const { return Factories.end(); }
+  FactoryMap::const_iterator find(StringRef Name) const {
+    return Factories.find(Name);
+  }
+  bool empty() const { return Factories.empty(); }
+
+private:
+  FactoryMap Factories;
+};
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARDSTYLE_H
Index: clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/utils/HeaderGuardStyle.cpp
@@ -0,0 +1,26 @@
+//===--- HeaderGuardStyle.cpp - clang-tidy ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGuardStyle.h"
+#include "../readability/HeaderGuardCheck.h"
+#include "../utils/FileExtensionsUtils.h"
+
+namespace clang::tidy::utils {
+bool HeaderGuardStyle::shouldFixIfHeaderGuard(StringRef Filename) {
+  return true;
+}
+
+bool HeaderGuardStyle::shouldFixIfNoHeaderGuard(StringRef FileName) {
+  return utils::isFileExtension(FileName, Check->getHeaderFileExtensions());
+}
+
+void HeaderGuardStyleFactories::registerStyleFactory(StringRef Name,
+                                                     StyleFactory Factory) {
+  Factories.insert_or_assign(Name, std::move(Factory));
+}
+} // namespace clang::tidy::utils
Index: clang-tools-extra/clang-tidy/utils/HeaderGuard.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/HeaderGuard.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//===--- HeaderGuard.h - clang-tidy -----------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
-
-#include "../ClangTidyCheck.h"
-#include "../utils/FileExtensionsUtils.h"
-
-namespace clang::tidy::utils {
-
-/// Finds and fixes header guards.
-/// The check supports these options:
-///   - `HeaderFileExtensions`: a semicolon-separated list of filename
-///     extensions of header files (The filename extension should not contain
-///     "." prefix). ";h;hh;hpp;hxx" by default.
-///
-///     For extension-less header files, using an empty string or leaving an
-///     empty string between ";" if there are other filename extensions.
-class HeaderGuardCheck : public ClangTidyCheck {
-public:
-  HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context),
-        RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
-            "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
-    utils::parseFileExtensions(RawStringHeaderFileExtensions,
-                               HeaderFileExtensions,
-                               utils::defaultFileExtensionDelimiters());
-  }
-  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
-  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
-                           Preprocessor *ModuleExpanderPP) override;
-
-  /// Ensure that the provided header guard is a non-reserved identifier.
-  std::string sanitizeHeaderGuard(StringRef Guard);
-
-  /// Returns ``true`` if the check should suggest inserting a trailing comment
-  /// on the ``#endif`` of the header guard. It will use the same name as
-  /// returned by ``HeaderGuardCheck::getHeaderGuard``.
-  virtual bool shouldSuggestEndifComment(StringRef Filename);
-  /// Returns ``true`` if the check should suggest changing an existing header
-  /// guard to the string returned by ``HeaderGuardCheck::getHeaderGuard``.
-  virtual bool shouldFixHeaderGuard(StringRef Filename);
-  /// Returns ``true`` if the check should add a header guard to the file
-  /// if it has none.
-  virtual bool shouldSuggestToAddHeaderGuard(StringRef Filename);
-  /// Returns a replacement for the ``#endif`` line with a comment mentioning
-  /// \p HeaderGuard. The replacement should start with ``endif``.
-  virtual std::string formatEndIf(StringRef HeaderGuard);
-  /// Gets the canonical header guard for a file.
-  virtual std::string getHeaderGuard(StringRef Filename,
-                                     StringRef OldGuard = StringRef()) = 0;
-
-private:
-  std::string RawStringHeaderFileExtensions;
-  utils::FileExtensionsSet HeaderFileExtensions;
-};
-
-} // namespace clang::tidy::utils
-
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
Index: clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
+++ /dev/null
@@ -1,300 +0,0 @@
-//===--- HeaderGuard.cpp - clang-tidy -------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "HeaderGuard.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Lex/PPCallbacks.h"
-#include "clang/Lex/Preprocessor.h"
-#include "clang/Tooling/Tooling.h"
-#include "llvm/Support/Path.h"
-
-namespace clang::tidy::utils {
-
-/// canonicalize a path by removing ./ and ../ components.
-static std::string cleanPath(StringRef Path) {
-  SmallString<256> Result = Path;
-  llvm::sys::path::remove_dots(Result, true);
-  return std::string(Result.str());
-}
-
-namespace {
-class HeaderGuardPPCallbacks : public PPCallbacks {
-public:
-  HeaderGuardPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check)
-      : PP(PP), Check(Check) {}
-
-  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
-                   SrcMgr::CharacteristicKind FileType,
-                   FileID PrevFID) override {
-    // Record all files we enter. We'll need them to diagnose headers without
-    // guards.
-    SourceManager &SM = PP->getSourceManager();
-    if (Reason == EnterFile && FileType == SrcMgr::C_User) {
-      if (const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
-        std::string FileName = cleanPath(FE->getName());
-        Files[FileName] = FE;
-      }
-    }
-  }
-
-  void Ifndef(SourceLocation HashLoc, SourceLocation Loc,
-              const Token &MacroNameTok, const MacroDefinition &MD) override {
-    if (MD)
-      return;
-
-    // Record #ifndefs that succeeded. We also need the Location of the Name.
-    Ifndefs[MacroNameTok.getIdentifierInfo()] =
-        std::make_pair(Loc, MacroNameTok.getLocation());
-  }
-
-  void MacroDefined(SourceLocation HashLoc, const Token &MacroNameTok,
-                    const MacroDirective *MD) override {
-    // Record all defined macros. We store the whole token to get info on the
-    // name later.
-    Macros.emplace_back(MacroNameTok, MD->getMacroInfo());
-  }
-
-  void Endif(SourceLocation HashLoc, SourceLocation Loc,
-             SourceLocation IfLoc) override {
-    // Record all #endif and the corresponding #ifs (including #ifndefs).
-    EndIfs[IfLoc] = Loc;
-  }
-
-  void EndOfMainFile() override {
-    // Now that we have all this information from the preprocessor, use it!
-    SourceManager &SM = PP->getSourceManager();
-
-    for (const auto &MacroEntry : Macros) {
-      const MacroInfo *MI = MacroEntry.second;
-
-      // We use clang's header guard detection. This has the advantage of also
-      // emitting a warning for cases where a pseudo header guard is found but
-      // preceded by something blocking the header guard optimization.
-      if (!MI->isUsedForHeaderGuard())
-        continue;
-
-      const FileEntry *FE =
-          SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
-      std::string FileName = cleanPath(FE->getName());
-      Files.erase(FileName);
-
-      // See if we should check and fix this header guard.
-      if (!Check->shouldFixHeaderGuard(FileName))
-        continue;
-
-      // Look up Locations for this guard.
-      SourceLocation Ifndef =
-          Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
-      SourceLocation Define = MacroEntry.first.getLocation();
-      SourceLocation EndIf =
-          EndIfs[Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
-
-      // If the macro Name is not equal to what we can compute, correct it in
-      // the #ifndef and #define.
-      StringRef CurHeaderGuard =
-          MacroEntry.first.getIdentifierInfo()->getName();
-      std::vector<FixItHint> FixIts;
-      std::string NewGuard = checkHeaderGuardDefinition(
-          Ifndef, Define, EndIf, FileName, CurHeaderGuard, FixIts);
-
-      // Now look at the #endif. We want a comment with the header guard. Fix it
-      // at the slightest deviation.
-      checkEndifComment(FileName, EndIf, NewGuard, FixIts);
-
-      // Bundle all fix-its into one warning. The message depends on whether we
-      // changed the header guard or not.
-      if (!FixIts.empty()) {
-        if (CurHeaderGuard != NewGuard) {
-          Check->diag(Ifndef, "header guard does not follow preferred style")
-              << FixIts;
-        } else {
-          Check->diag(EndIf, "#endif for a header guard should reference the "
-                             "guard macro in a comment")
-              << FixIts;
-        }
-      }
-    }
-
-    // Emit warnings for headers that are missing guards.
-    checkGuardlessHeaders();
-    clearAllState();
-  }
-
-  bool wouldFixEndifComment(StringRef FileName, SourceLocation EndIf,
-                            StringRef HeaderGuard,
-                            size_t *EndIfLenPtr = nullptr) {
-    if (!EndIf.isValid())
-      return false;
-    const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
-    size_t EndIfLen = std::strcspn(EndIfData, "\r\n");
-    if (EndIfLenPtr)
-      *EndIfLenPtr = EndIfLen;
-
-    StringRef EndIfStr(EndIfData, EndIfLen);
-    EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of("#endif \t"));
-
-    // Give up if there's an escaped newline.
-    size_t FindEscapedNewline = EndIfStr.find_last_not_of(' ');
-    if (FindEscapedNewline != StringRef::npos &&
-        EndIfStr[FindEscapedNewline] == '\\')
-      return false;
-
-    bool IsLineComment =
-        EndIfStr.consume_front("//") ||
-        (EndIfStr.consume_front("/*") && EndIfStr.consume_back("*/"));
-    if (!IsLineComment)
-      return Check->shouldSuggestEndifComment(FileName);
-
-    return EndIfStr.trim() != HeaderGuard;
-  }
-
-  /// Look for header guards that don't match the preferred style. Emit
-  /// fix-its and return the suggested header guard (or the original if no
-  /// change was made.
-  std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
-                                         SourceLocation Define,
-                                         SourceLocation EndIf,
-                                         StringRef FileName,
-                                         StringRef CurHeaderGuard,
-                                         std::vector<FixItHint> &FixIts) {
-    std::string CPPVar = Check->getHeaderGuard(FileName, CurHeaderGuard);
-    CPPVar = Check->sanitizeHeaderGuard(CPPVar);
-    std::string CPPVarUnder = CPPVar + '_';
-
-    // Allow a trailing underscore if and only if we don't have to change the
-    // endif comment too.
-    if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
-        (CurHeaderGuard != CPPVarUnder ||
-         wouldFixEndifComment(FileName, EndIf, CurHeaderGuard))) {
-      FixIts.push_back(FixItHint::CreateReplacement(
-          CharSourceRange::getTokenRange(
-              Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
-          CPPVar));
-      FixIts.push_back(FixItHint::CreateReplacement(
-          CharSourceRange::getTokenRange(
-              Define, Define.getLocWithOffset(CurHeaderGuard.size())),
-          CPPVar));
-      return CPPVar;
-    }
-    return std::string(CurHeaderGuard);
-  }
-
-  /// Checks the comment after the #endif of a header guard and fixes it
-  /// if it doesn't match \c HeaderGuard.
-  void checkEndifComment(StringRef FileName, SourceLocation EndIf,
-                         StringRef HeaderGuard,
-                         std::vector<FixItHint> &FixIts) {
-    size_t EndIfLen;
-    if (wouldFixEndifComment(FileName, EndIf, HeaderGuard, &EndIfLen)) {
-      FixIts.push_back(FixItHint::CreateReplacement(
-          CharSourceRange::getCharRange(EndIf,
-                                        EndIf.getLocWithOffset(EndIfLen)),
-          Check->formatEndIf(HeaderGuard)));
-    }
-  }
-
-  /// Looks for files that were visited but didn't have a header guard.
-  /// Emits a warning with fixits suggesting adding one.
-  void checkGuardlessHeaders() {
-    // Look for header files that didn't have a header guard. Emit a warning and
-    // fix-its to add the guard.
-    // TODO: Insert the guard after top comments.
-    for (const auto &FE : Files) {
-      StringRef FileName = FE.getKey();
-      if (!Check->shouldSuggestToAddHeaderGuard(FileName))
-        continue;
-
-      SourceManager &SM = PP->getSourceManager();
-      FileID FID = SM.translateFile(FE.getValue());
-      SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
-      if (StartLoc.isInvalid())
-        continue;
-
-      std::string CPPVar = Check->getHeaderGuard(FileName);
-      CPPVar = Check->sanitizeHeaderGuard(CPPVar);
-      std::string CPPVarUnder = CPPVar + '_'; // Allow a trailing underscore.
-      // If there's a macro with a name that follows the header guard convention
-      // but was not recognized by the preprocessor as a header guard there must
-      // be code outside of the guarded area. Emit a plain warning without
-      // fix-its.
-      // FIXME: Can we move it into the right spot?
-      bool SeenMacro = false;
-      for (const auto &MacroEntry : Macros) {
-        StringRef Name = MacroEntry.first.getIdentifierInfo()->getName();
-        SourceLocation DefineLoc = MacroEntry.first.getLocation();
-        if ((Name == CPPVar || Name == CPPVarUnder) &&
-            SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
-          Check->diag(DefineLoc, "code/includes outside of area guarded by "
-                                 "header guard; consider moving it");
-          SeenMacro = true;
-          break;
-        }
-      }
-
-      if (SeenMacro)
-        continue;
-
-      Check->diag(StartLoc, "header is missing header guard")
-          << FixItHint::CreateInsertion(
-                 StartLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + "\n\n")
-          << FixItHint::CreateInsertion(
-                 SM.getLocForEndOfFile(FID),
-                 Check->shouldSuggestEndifComment(FileName)
-                     ? "\n#" + Check->formatEndIf(CPPVar) + "\n"
-                     : "\n#endif\n");
-    }
-  }
-
-private:
-  void clearAllState() {
-    Macros.clear();
-    Files.clear();
-    Ifndefs.clear();
-    EndIfs.clear();
-  }
-
-  std::vector<std::pair<Token, const MacroInfo *>> Macros;
-  llvm::StringMap<const FileEntry *> Files;
-  std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
-      Ifndefs;
-  std::map<SourceLocation, SourceLocation> EndIfs;
-
-  Preprocessor *PP;
-  HeaderGuardCheck *Check;
-};
-} // namespace
-
-void HeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
-  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
-}
-
-void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM,
-                                           Preprocessor *PP,
-                                           Preprocessor *ModuleExpanderPP) {
-  PP->addPPCallbacks(std::make_unique<HeaderGuardPPCallbacks>(PP, this));
-}
-
-std::string HeaderGuardCheck::sanitizeHeaderGuard(StringRef Guard) {
-  // Only reserved identifiers are allowed to start with an '_'.
-  return Guard.drop_while([](char C) { return C == '_'; }).str();
-}
-
-bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef FileName) {
-  return utils::isFileExtension(FileName, HeaderFileExtensions);
-}
-
-bool HeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) { return true; }
-
-bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef FileName) {
-  return utils::isFileExtension(FileName, HeaderFileExtensions);
-}
-
-std::string HeaderGuardCheck::formatEndIf(StringRef HeaderGuard) {
-  return "endif // " + HeaderGuard.str();
-}
-} // namespace clang::tidy::utils
Index: clang-tools-extra/clang-tidy/utils/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/utils/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/utils/CMakeLists.txt
@@ -11,10 +11,11 @@
   ExprSequence.cpp
   FileExtensionsUtils.cpp
   FixItHintUtils.cpp
-  HeaderGuard.cpp
+  HeaderGuardStyle.cpp
   IncludeInserter.cpp
   IncludeSorter.cpp
   LexerUtils.cpp
+  MacroHeaderGuardStyle.cpp
   NamespaceAliaser.cpp
   OptionsUtils.cpp
   RenamerClangTidyCheck.cpp
Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -21,6 +21,7 @@
 #include "ElseAfterReturnCheck.h"
 #include "FunctionCognitiveComplexityCheck.h"
 #include "FunctionSizeCheck.h"
+#include "HeaderGuardCheck.h"
 #include "IdentifierLengthCheck.h"
 #include "IdentifierNamingCheck.h"
 #include "ImplicitBoolConversionCheck.h"
@@ -82,6 +83,7 @@
         "readability-function-cognitive-complexity");
     CheckFactories.registerCheck<FunctionSizeCheck>(
         "readability-function-size");
+    CheckFactories.registerCheck<HeaderGuardCheck>("readability-header-guard");
     CheckFactories.registerCheck<IdentifierLengthCheck>(
         "readability-identifier-length");
     CheckFactories.registerCheck<IdentifierNamingCheck>(
Index: clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.h
@@ -0,0 +1,51 @@
+//===--- HeaderGuard.h - clang-tidy -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_HEADERGUARDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_HEADERGUARDCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/FileExtensionsUtils.h"
+#include "../utils/HeaderGuardStyle.h"
+
+namespace clang::tidy::readability {
+
+/// Finds and fixes header guards.
+/// The check supports these options:
+///   - `Style`: the name of a header guard style to use. The only available
+///     option is "llvm". "llvm" by default.
+class HeaderGuardCheck : public ClangTidyCheck {
+public:
+  HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
+            "HeaderFileExtensions", utils::defaultHeaderFileExtensions())),
+        StyleName(Options.getLocalOrGlobal("Style", "llvm")) {
+    utils::parseFileExtensions(RawStringHeaderFileExtensions,
+                               HeaderFileExtensions,
+                               utils::defaultFileExtensionDelimiters());
+  }
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  utils::FileExtensionsSet &getHeaderFileExtensions() {
+    return HeaderFileExtensions;
+  }
+
+protected:
+  virtual std::unique_ptr<utils::HeaderGuardStyle> createHeaderGuardStyle();
+
+  std::string RawStringHeaderFileExtensions;
+  utils::FileExtensionsSet HeaderFileExtensions;
+  std::string StyleName;
+  std::unique_ptr<utils::HeaderGuardStyle> Style;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_HEADERGUARDCHECK_H
Index: clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/HeaderGuardCheck.cpp
@@ -0,0 +1,182 @@
+//===--- HeaderGuard.cpp - clang-tidy -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGuardCheck.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+
+namespace clang::tidy::readability {
+
+/// canonicalize a path by removing ./ and ../ components.
+static std::string cleanPath(StringRef Path) {
+  SmallString<256> Result = Path;
+  llvm::sys::path::remove_dots(Result, true);
+  return std::string(Result.str());
+}
+
+namespace {
+class HeaderGuardCheckPPCallbacks : public PPCallbacks {
+public:
+  HeaderGuardCheckPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check,
+                              utils::HeaderGuardStyle *Style)
+      : PP(PP), Check(Check), Style(Style) {}
+
+  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+                   SrcMgr::CharacteristicKind FileType,
+                   FileID PrevFID) override {
+    // Record all files we enter. We'll need them to diagnose headers without
+    // guards.
+    SourceManager &SM = PP->getSourceManager();
+    if (Reason == EnterFile && FileType == SrcMgr::C_User) {
+      if (const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
+        std::string FileName = cleanPath(FE->getName());
+        Files[FileName] = FE;
+      }
+    }
+  }
+
+  void Ifndef(SourceLocation HashLoc, SourceLocation Loc,
+              const Token &MacroNameTok, const MacroDefinition &MD) override {
+    if (MD)
+      return;
+
+    // Record #ifndefs that succeeded. We also need the Location of the Name.
+    Ifndefs[MacroNameTok.getIdentifierInfo()] =
+        std::make_tuple(HashLoc, Loc, MacroNameTok.getLocation());
+  }
+
+  void MacroDefined(SourceLocation HashLoc, const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    // Record all defined macros. We store the whole token to get info on the
+    // name later.
+    Macros.emplace_back(HashLoc, MacroNameTok, MD->getMacroInfo());
+  }
+
+  void Endif(SourceLocation HashLoc, SourceLocation Loc,
+             SourceLocation IfLoc) override {
+    // Record all #endif and the corresponding #ifs (including #ifndefs).
+    EndIfs[IfLoc] = std::make_pair(HashLoc, Loc);
+  }
+
+  void EndOfMainFile() override {
+    // Now that we have all this information from the preprocessor, use it!
+    SourceManager &SM = PP->getSourceManager();
+
+    for (const auto &MacroEntry : Macros) {
+      const MacroInfo *MI = std::get<2>(MacroEntry);
+
+      // We use clang's header guard detection. This has the advantage of also
+      // emitting a warning for cases where a pseudo header guard is found but
+      // preceded by something blocking the header guard optimization.
+      if (!MI->isUsedForHeaderGuard())
+        continue;
+
+      const FileEntry *FE =
+          SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
+      std::string FileName = cleanPath(FE->getName());
+      Files.erase(FileName);
+
+      // See if we should check and fix this header guard.
+      if (!Style->shouldFixIfHeaderGuard(FileName))
+        continue;
+
+      // Look up Locations for this guard.
+      std::tuple<SourceLocation, SourceLocation, SourceLocation> Ifndef =
+          Ifndefs[std::get<1>(MacroEntry).getIdentifierInfo()];
+      std::pair<SourceLocation, SourceLocation> EndIf =
+          EndIfs[std::get<1>(Ifndef)];
+
+      Style->onHeaderGuard(PP, FileName, FE, std::get<0>(Ifndef),
+                           std::get<1>(Ifndef), std::get<2>(Ifndef),
+                           std::get<0>(MacroEntry), std::get<1>(MacroEntry),
+                           EndIf.first, EndIf.second);
+    }
+
+    // Emit warnings for headers that are missing guards.
+    checkGuardlessHeaders();
+    clearAllState();
+  }
+
+  /// Looks for files that were visited but didn't have a header guard.
+  /// Emits a warning with fixits suggesting adding one.
+  void checkGuardlessHeaders() {
+    // Look for header files that didn't have a header guard. Emit a warning and
+    // fix-its to add the guard.
+    // TODO: Insert the guard after top comments.
+    for (const auto &FE : Files) {
+      StringRef FileName = FE.getKey();
+      if (!Style->shouldFixIfNoHeaderGuard(FileName))
+        continue;
+
+      SourceManager &SM = PP->getSourceManager();
+      FileID FID = SM.translateFile(FE.getValue());
+      SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
+      if (StartLoc.isInvalid())
+        continue;
+
+      Style->onGuardlessHeader(PP, FileName, FE.getValue(), StartLoc, Macros);
+    }
+  }
+
+private:
+  void clearAllState() {
+    Macros.clear();
+    Files.clear();
+    Ifndefs.clear();
+    EndIfs.clear();
+  }
+
+  std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>> Macros;
+  llvm::StringMap<const FileEntry *> Files;
+  std::map<const IdentifierInfo *,
+           std::tuple<SourceLocation, SourceLocation, SourceLocation>>
+      Ifndefs;
+  std::map<SourceLocation, std::pair<SourceLocation, SourceLocation>> EndIfs;
+
+  Preprocessor *PP;
+  HeaderGuardCheck *Check;
+  utils::HeaderGuardStyle *Style;
+};
+} // namespace
+
+void HeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Style", StyleName);
+}
+
+void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM,
+                                           Preprocessor *PP,
+                                           Preprocessor *ModuleExpanderPP) {
+  Style = createHeaderGuardStyle();
+  if (Style)
+    PP->addPPCallbacks(
+        std::make_unique<HeaderGuardCheckPPCallbacks>(PP, this, Style.get()));
+}
+
+std::unique_ptr<utils::HeaderGuardStyle>
+HeaderGuardCheck::createHeaderGuardStyle() {
+  utils::HeaderGuardStyleFactories StyleFactories;
+  for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
+    E.instantiate()->addHeaderGuardStyleFactories(StyleFactories);
+  }
+
+  auto It = StyleFactories.find(StyleName);
+  if (It == StyleFactories.end()) {
+    std::string MsgStr;
+    llvm::raw_string_ostream Msg(MsgStr);
+    Msg << "no such header guard style: '" << StyleName << "'";
+    configurationDiag(Msg.str(), DiagnosticIDs::Error);
+    return nullptr;
+  }
+
+  return It->second(this);
+}
+} // namespace clang::tidy::readability
Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -16,6 +16,7 @@
   ElseAfterReturnCheck.cpp
   FunctionCognitiveComplexityCheck.cpp
   FunctionSizeCheck.cpp
+  HeaderGuardCheck.cpp
   IdentifierLengthCheck.cpp
   IdentifierNamingCheck.cpp
   ImplicitBoolConversionCheck.cpp
Index: clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
+++ clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
@@ -13,6 +13,7 @@
 #include "../readability/NamespaceCommentCheck.h"
 #include "../readability/QualifiedAutoCheck.h"
 #include "HeaderGuardCheck.h"
+#include "HeaderGuardStyle.h"
 #include "IncludeOrderCheck.h"
 #include "PreferIsaOrDynCastInConditionalsCheck.h"
 #include "PreferRegisterOverUnsignedCheck.h"
@@ -39,6 +40,11 @@
     CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
   }
 
+  void addHeaderGuardStyleFactories(
+      utils::HeaderGuardStyleFactories &StyleFactories) override {
+    StyleFactories.registerStyle<LLVMHeaderGuardStyle>("llvm");
+  }
+
   ClangTidyOptions getModuleOptions() override {
     ClangTidyOptions Options;
     Options.CheckOptions["llvm-qualified-auto.AddConstToQualified"] = "false";
Index: clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.h
@@ -0,0 +1,36 @@
+//===--- HeaderGuardStyle.h - clang-tidy ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADERGUARDSTYLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADERGUARDSTYLE_H
+
+#include "../utils/MacroHeaderGuardStyle.h"
+
+namespace clang::tidy::llvm_check {
+
+/// Finds and fixes header guards that do not adhere to LLVM style.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/llvm/header-guard.html
+/// The check supports these options:
+///   - `HeaderFileExtensions`: a semicolon-separated list of filename
+///     extensions of header files (The filename extension should not contain
+///     "." prefix). ";h;hh;hpp;hxx" by default.
+///
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between ";" if there are other filename extensions.
+class LLVMHeaderGuardStyle : public utils::MacroHeaderGuardStyle {
+public:
+  LLVMHeaderGuardStyle(readability::HeaderGuardCheck *Check);
+
+  bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
+  std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+};
+
+} // namespace clang::tidy::llvm_check
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADERGUARDSTYLE_H
Index: clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/llvm/HeaderGuardStyle.cpp
@@ -0,0 +1,66 @@
+//===--- HeaderGuardCheck.cpp - clang-tidy --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGuardStyle.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+
+namespace clang::tidy::llvm_check {
+
+LLVMHeaderGuardStyle::LLVMHeaderGuardStyle(readability::HeaderGuardCheck *Check)
+    : MacroHeaderGuardStyle(Check) {}
+
+std::string LLVMHeaderGuardStyle::getHeaderGuard(StringRef Filename,
+                                                 StringRef OldGuard) {
+  std::string Guard = tooling::getAbsolutePath(Filename);
+
+  // When running under Windows, need to convert the path separators from
+  // `\` to `/`.
+  Guard = llvm::sys::path::convert_to_slash(Guard);
+
+  // 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.
+  size_t PosInclude = Guard.rfind("include/");
+  if (PosInclude != StringRef::npos)
+    Guard = Guard.substr(PosInclude + std::strlen("include/"));
+
+  // For clang we drop the _TOOLS_.
+  size_t PosToolsClang = Guard.rfind("tools/clang/");
+  if (PosToolsClang != StringRef::npos)
+    Guard = Guard.substr(PosToolsClang + std::strlen("tools/"));
+
+  // Unlike LLVM svn, LLVM git monorepo is named llvm-project, so we replace
+  // "/llvm-project/" with the canonical "/llvm/".
+  const static StringRef LLVMProject = "/llvm-project/";
+  size_t PosLLVMProject = Guard.rfind(std::string(LLVMProject));
+  if (PosLLVMProject != StringRef::npos)
+    Guard = Guard.replace(PosLLVMProject, LLVMProject.size(), "/llvm/");
+
+  // The remainder is LLVM_FULL_PATH_TO_HEADER_H
+  size_t PosLLVM = Guard.rfind("llvm/");
+  if (PosLLVM != StringRef::npos)
+    Guard = Guard.substr(PosLLVM);
+
+  std::replace(Guard.begin(), Guard.end(), '/', '_');
+  std::replace(Guard.begin(), Guard.end(), '.', '_');
+  std::replace(Guard.begin(), Guard.end(), '-', '_');
+
+  // The prevalent style in clang is LLVM_CLANG_FOO_BAR_H
+  if (StringRef(Guard).startswith("clang"))
+    Guard = "LLVM_" + Guard;
+
+  // The prevalent style in flang is FORTRAN_FOO_BAR_H
+  if (StringRef(Guard).startswith("flang"))
+    Guard = "FORTRAN" + Guard.substr(sizeof("flang") - 1);
+
+  return StringRef(Guard).upper();
+}
+
+} // namespace clang::tidy::llvm_check
Index: clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
===================================================================
--- clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
+++ clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.h
@@ -9,7 +9,7 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADERGUARDCHECK_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADERGUARDCHECK_H
 
-#include "../utils/HeaderGuard.h"
+#include "../readability/HeaderGuardCheck.h"
 
 namespace clang::tidy::llvm_check {
 
@@ -23,12 +23,13 @@
 ///
 ///     For extension-less header files, using an empty string or leaving an
 ///     empty string between ";" if there are other filename extensions.
-class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
+class LLVMHeaderGuardCheck : public readability::HeaderGuardCheck {
 public:
   LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
 
-  bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
-  std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+protected:
+  std::unique_ptr<utils::HeaderGuardStyle> createHeaderGuardStyle() override;
 };
 
 } // namespace clang::tidy::llvm_check
Index: clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
+++ clang-tools-extra/clang-tidy/llvm/HeaderGuardCheck.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "HeaderGuardCheck.h"
+#include "HeaderGuardStyle.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/Path.h"
 
@@ -16,52 +17,13 @@
                                            ClangTidyContext *Context)
     : HeaderGuardCheck(Name, Context) {}
 
-std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename,
-                                                 StringRef OldGuard) {
-  std::string Guard = tooling::getAbsolutePath(Filename);
-
-  // When running under Windows, need to convert the path separators from
-  // `\` to `/`.
-  Guard = llvm::sys::path::convert_to_slash(Guard);
-
-  // 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.
-  size_t PosInclude = Guard.rfind("include/");
-  if (PosInclude != StringRef::npos)
-    Guard = Guard.substr(PosInclude + std::strlen("include/"));
-
-  // For clang we drop the _TOOLS_.
-  size_t PosToolsClang = Guard.rfind("tools/clang/");
-  if (PosToolsClang != StringRef::npos)
-    Guard = Guard.substr(PosToolsClang + std::strlen("tools/"));
-
-  // Unlike LLVM svn, LLVM git monorepo is named llvm-project, so we replace
-  // "/llvm-project/" with the canonical "/llvm/".
-  const static StringRef LLVMProject = "/llvm-project/";
-  size_t PosLLVMProject = Guard.rfind(std::string(LLVMProject));
-  if (PosLLVMProject != StringRef::npos)
-    Guard = Guard.replace(PosLLVMProject, LLVMProject.size(), "/llvm/");
-
-  // The remainder is LLVM_FULL_PATH_TO_HEADER_H
-  size_t PosLLVM = Guard.rfind("llvm/");
-  if (PosLLVM != StringRef::npos)
-    Guard = Guard.substr(PosLLVM);
-
-  std::replace(Guard.begin(), Guard.end(), '/', '_');
-  std::replace(Guard.begin(), Guard.end(), '.', '_');
-  std::replace(Guard.begin(), Guard.end(), '-', '_');
-
-  // The prevalent style in clang is LLVM_CLANG_FOO_BAR_H
-  if (StringRef(Guard).startswith("clang"))
-    Guard = "LLVM_" + Guard;
-
-  // The prevalent style in flang is FORTRAN_FOO_BAR_H
-  if (StringRef(Guard).startswith("flang"))
-    Guard = "FORTRAN" + Guard.substr(sizeof("flang") - 1);
+void LLVMHeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
 
-  return StringRef(Guard).upper();
+std::unique_ptr<utils::HeaderGuardStyle>
+LLVMHeaderGuardCheck::createHeaderGuardStyle() {
+  return std::make_unique<LLVMHeaderGuardStyle>(this);
 }
 
 } // namespace clang::tidy::llvm_check
Index: clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_clang_library(clangTidyLLVMModule
   HeaderGuardCheck.cpp
+  HeaderGuardStyle.cpp
   IncludeOrderCheck.cpp
   LLVMTidyModule.cpp
   PreferIsaOrDynCastInConditionalsCheck.cpp
Index: clang-tools-extra/clang-tidy/ClangTidyModule.h
===================================================================
--- clang-tools-extra/clang-tidy/ClangTidyModule.h
+++ clang-tools-extra/clang-tidy/ClangTidyModule.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
 
 #include "ClangTidyOptions.h"
+#include "utils/HeaderGuardStyle.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include <functional>
@@ -89,6 +90,11 @@
   /// belonging to this module.
   virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
 
+  /// Implement this function in order to register all \c StyleFactories
+  /// belonging to this module.
+  virtual void addHeaderGuardStyleFactories(
+      utils::HeaderGuardStyleFactories &StyleFactories){};
+
   /// Gets default options for checks defined in this module.
   virtual ClangTidyOptions getModuleOptions();
 };
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to