KyleFromKitware updated this revision to Diff 494649.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D142673/new/
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/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
clang-tools-extra/docs/clang-tidy/checks/readability/header-guard.rst
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/docs/clang-tidy/checks/readability/header-guard.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/readability/header-guard.rst
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - readability-header-guard
+
+readability-header-guard
+========================
+
+Finds and fixes header guards that do not adhere to a specified style.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+ A comma-separated list of filename extensions of header files (the filename
+ extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+ For header files without an extension, use an empty string (if there are no
+ other desired extensions) or leave an empty element in the list. E.g.,
+ "h,hh,hpp,hxx," (note the trailing comma).
+
+.. option:: Style
+
+ The name of a header guard style to select. The default is "llvm". Available
+ options are:
+
+ ``llvm``
+
+ Use the LLVM header guard style.
Index: clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
+++ clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
@@ -5,6 +5,10 @@
Finds and fixes header guards that do not adhere to LLVM style.
+Note: this check is deprecated, it will be removed in :program:`clang-tidy`
+version 19. Please use the check `readability-header-guard`. with the ``llvm``
+style.
+
Options
-------
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -339,6 +339,7 @@
`readability-else-after-return <readability/else-after-return.html>`_, "Yes"
`readability-function-cognitive-complexity <readability/function-cognitive-complexity.html>`_,
`readability-function-size <readability/function-size.html>`_,
+ `readability-header-guard <readability/header-guard.html>`_,
`readability-identifier-length <readability/identifier-length.html>`_,
`readability-identifier-naming <readability/identifier-naming.html>`_, "Yes"
`readability-implicit-bool-conversion <readability/implicit-bool-conversion.html>`_, "Yes"
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -110,6 +110,11 @@
Warns when lambda specify a capture default and capture ``this``.
+- New :doc:`readability-header-guard
+ <clang-tidy/checks/readability/header-guard>` check.
+
+ Finds header guards and corrects them if necessary.
+
New check aliases
^^^^^^^^^^^^^^^^^
@@ -124,6 +129,11 @@
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Deprecated :doc:`llvm-header-guard
+ <clang-tidy/checks/llvm/header-guard>` check. Use
+ :doc:`readability-header-guard
+ <clang-tidy/checks/readability/header-guard>` with the ``llvm`` style instead.
+
Removed checks
^^^^^^^^^^^^^^
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 @@
+//===--- HeaderGuardCheck.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 @@
+//===--- 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 "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 @@
+//===--- HeaderGuardStyle.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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits