https://github.com/thorsten-klein updated https://github.com/llvm/llvm-project/pull/177315
>From 21012ea379fe2c627fbb9b7bf4c535e4c5fd704d Mon Sep 17 00:00:00 2001 From: Thorsten Klein <[email protected]> Date: Thu, 22 Jan 2026 08:33:13 +0100 Subject: [PATCH 1/2] [clang-tidy] Add a new check `misc-header-guard` Find and fix header guards It respects option `misc-header-guard.HeaderDirs`, which contains a list of one or more header directory names. --- .../clang-tidy/misc/CMakeLists.txt | 1 + .../clang-tidy/misc/HeaderGuardCheck.cpp | 105 +++++++++ .../clang-tidy/misc/HeaderGuardCheck.h | 46 ++++ .../clang-tidy/misc/MiscTidyModule.cpp | 2 + clang-tools-extra/docs/ReleaseNotes.rst | 4 + .../clang-tidy/checks/misc/header-guard.rst | 90 ++++++++ .../clang-tidy/checkers/misc/header-guard.cpp | 209 ++++++++++++++++++ .../misc/header-guard/include/correct.hpp | 7 + .../misc/header-guard/include/missing.hpp | 3 + .../header-guard/include/other/correct.hpp | 7 + .../header-guard/include/other/missing.hpp | 3 + .../misc/header-guard/include/other/wrong.hpp | 7 + .../misc/header-guard/include/pragma-once.hpp | 10 + .../misc/header-guard/include/wrong.hpp | 7 + 14 files changed, 501 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/misc/header-guard.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/correct.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/missing.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/correct.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/missing.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/wrong.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/pragma-once.hpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/wrong.hpp diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index e34b0cf687be3..f8cb11bd3b8c1 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangTidyMiscModule STATIC CoroutineHostileRAIICheck.cpp DefinitionsInHeadersCheck.cpp ConfusableIdentifierCheck.cpp + HeaderGuardCheck.cpp HeaderIncludeCycleCheck.cpp IncludeCleanerCheck.cpp MiscTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.cpp b/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.cpp new file mode 100644 index 0000000000000..ba5939adf36a0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../utils/OptionsUtils.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Path.h" + +namespace clang::tidy::misc { + +HeaderGuardCheck::HeaderGuardCheck(StringRef Name, ClangTidyContext *Context) + : clang::tidy::utils::HeaderGuardCheck(Name, Context), + AllowPragmaOnce(Options.get("AllowPragmaOnce", false)), + HeaderDirs(utils::options::parseStringList( + Options.get("HeaderDirs", "include"))), + EndifComment(Options.get("EndifComment", false)), + Prefix(Options.get("Prefix", "")) {} + +std::string HeaderGuardCheck::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); + + // consider all directories from HeaderDirs option. Stop at first found. + for (const StringRef HeaderDir : HeaderDirs) { + const size_t PosHeaderDir = Guard.rfind("/" + HeaderDir.str() + "/"); + if (PosHeaderDir != StringRef::npos) { + // We don't want the header dir in our guards, i.e. _INCLUDE_ + Guard = Guard.substr(PosHeaderDir + HeaderDir.size() + 2); + break; // stop at first found + } + } + + llvm::replace(Guard, '/', '_'); + llvm::replace(Guard, '.', '_'); + llvm::replace(Guard, '-', '_'); + + Guard = Prefix.str() + Guard; + + return StringRef(Guard).upper(); +} + +bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef Filename) { + return EndifComment; +} + +bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef Filename) { + if (HasPragmaOnce && AllowPragmaOnce) + return false; + return utils::HeaderGuardCheck::shouldSuggestToAddHeaderGuard(Filename); +} + +void HeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowPragmaOnce", AllowPragmaOnce); + Options.store(Opts, "EndifComment", EndifComment); + Options.store(Opts, "HeaderDirs", + utils::options::serializeStringList(HeaderDirs)); + Options.store(Opts, "Prefix", Prefix); +} + +class HeaderGuardCallbacks : public PPCallbacks { +public: + HeaderGuardCallbacks(HeaderGuardCheck *Check, const SourceManager &SM) + : Check(Check), SM(SM) {} + void PragmaDirective(SourceLocation Loc, + PragmaIntroducerKind Introducer) override { + auto Str = StringRef(SM.getCharacterData(Loc)); + if (!Str.consume_front("#")) + return; + Str = Str.trim(); + if (!Str.consume_front("pragma")) + return; + Str = Str.trim(); + if (Str.starts_with("once")) { + Check->HasPragmaOnce = true; + if (!Check->AllowPragmaOnce) + Check->diag(Loc, + "'pragma once' is not allowed; use include guards instead"); + } + } + +private: + HeaderGuardCheck *Check; + const SourceManager &SM; +}; + +void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + utils::HeaderGuardCheck::registerPPCallbacks(SM, PP, ModuleExpanderPP); + PP->addPPCallbacks(std::make_unique<HeaderGuardCallbacks>(this, SM)); +} + +} // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.h b/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.h new file mode 100644 index 0000000000000..426c0a88af571 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/HeaderGuardCheck.h @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// 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_MISC_HEADERGUARDCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_HEADERGUARDCHECK_H + +#include "../utils/HeaderGuard.h" + +namespace clang::tidy::misc { + +/// Finds and fixes header guards. +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/misc/header-guard.html +class HeaderGuardCheck : public utils::HeaderGuardCheck { +public: + HeaderGuardCheck(StringRef Name, ClangTidyContext *Context); + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus || LangOpts.C99; + } + + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + + bool shouldSuggestEndifComment(StringRef Filename) override; + bool shouldSuggestToAddHeaderGuard(StringRef Filename) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override; + + const bool AllowPragmaOnce; + bool HasPragmaOnce = false; + +private: + const std::vector<StringRef> HeaderDirs; + const bool EndifComment; + const StringRef Prefix; +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_HEADERGUARDCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index f8550b30b9789..8523031ef820e 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -13,6 +13,7 @@ #include "ConstCorrectnessCheck.h" #include "CoroutineHostileRAIICheck.h" #include "DefinitionsInHeadersCheck.h" +#include "HeaderGuardCheck.h" #include "HeaderIncludeCycleCheck.h" #include "IncludeCleanerCheck.h" #include "MisleadingBidirectionalCheck.h" @@ -53,6 +54,7 @@ class MiscModule : public ClangTidyModule { "misc-coroutine-hostile-raii"); CheckFactories.registerCheck<DefinitionsInHeadersCheck>( "misc-definitions-in-headers"); + CheckFactories.registerCheck<HeaderGuardCheck>("misc-header-guard"); CheckFactories.registerCheck<HeaderIncludeCycleCheck>( "misc-header-include-cycle"); CheckFactories.registerCheck<IncludeCleanerCheck>("misc-include-cleaner"); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 754880bd1a381..9a63c9586e459 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -116,6 +116,10 @@ New checks Finds and removes redundant conversions from ``std::[w|u8|u16|u32]string_view`` to ``std::[...]string`` in call expressions expecting ``std::[...]string_view``. +- New :doc:`misc-header-guard <clang-tidy/checks/misc/header-guard>` check. + + Finds and fixes header guards. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/header-guard.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/header-guard.rst new file mode 100644 index 0000000000000..c512cbe6fcbcb --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/header-guard.rst @@ -0,0 +1,90 @@ +.. title:: clang-tidy - llvm-header-guard + +misc-header-guard +================= + +Finds and fixes header guards. + +Options +------- + +.. option:: HeaderDirs + + A semicolon-separated list of one or more header directory names. Header + directories may contain `/` as path separator. The list is searched for the + first matching string. The header guard will start from this path + component. Default value is `include`. + +.. option:: Prefix + + A string specifying an optional prefix that is applied to each header guard. + Default is an empty string. + +.. option:: EndifComment + + A boolean that controls whether the endif namespace comment is suggested. + Default value is `false`. + +.. option:: AllowPragmaOnce + + A boolean that controls whether ``#pragma once`` directive is allowed. + Default value is `false`. + +Examples +-------- + +Header file: ``/path/to/include/component/header.hpp``: + +By default, the check ensures following header guard: + +.. code-block:: c++ + + #ifndef COMPONENT_HEADER_HPP + #define COMPONENT_HEADER_HPP + ... + # endif + +E.g. :option:`HeaderDirs` is set to following values: + +- `component` +- `include/component` +- `component;include` + +.. code-block:: c++ + + #ifndef HEADER_HPP + #define HEADER_HPP + ... + # endif + +.. warning:: + + The .:option:`HeaderDirs` list is searched until first directory name matches + the header file path. If `HeaderDirs` is set to `include;component`, the + check will result in default behavior (since `include` is found first). + +E.g. :option:`Prefix` is set to `MY_OWN_PREFIX_`: + +.. code-block:: c++ + + #ifndef MY_OWN_PREFIX_COMPONENT_HEADER_HPP + #define MY_OWN_PREFIX_COMPONENT_HEADER_HPP + ... + # endif + +E.g. :option:`EndifComment` is set to `true`: + +.. code-block:: c++ + + #ifndef COMPONENT_HEADER_HPP + #define COMPONENT_HEADER_HPP + ... + # endif // COMPONENT_HEADER_HPP + +E.g. with option :option:`AllowPragmaOnce` set to `true`, ``#pragma once`` is +allowed as header guard: + +.. code-block:: c++ + + #pragma once + ... diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard.cpp new file mode 100644 index 0000000000000..9304dc94a7de7 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard.cpp @@ -0,0 +1,209 @@ +#include "header-guard/include/correct.hpp" +#include "header-guard/include/missing.hpp" +#include "header-guard/include/wrong.hpp" + +#include "header-guard/include/other/correct.hpp" +#include "header-guard/include/other/missing.hpp" +#include "header-guard/include/other/wrong.hpp" + +// --------------------------------------- +// TEST 1: Use no config options (default) +// --------------------------------------- +// RUN: %check_clang_tidy %s misc-header-guard %t -export-fixes=%t.1.yaml --header-filter=.* -- -I%S > %t.1.msg 2>&1 +// RUN: FileCheck -input-file=%t.1.msg -check-prefix=CHECK-MESSAGES1 %s +// RUN: FileCheck -input-file=%t.1.yaml -check-prefix=CHECK-YAML1 %s + +// CHECK-MESSAGES1: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES1: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES1: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES1: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] + +// CHECK-YAML1: Message: header is missing header guard +// CHECK-YAML1: FilePath: '{{.*header-guard.include.}}missing.hpp' +// CHECK-YAML1: ReplacementText: "#ifndef MISSING_HPP\n#define MISSING_HPP\n\n" +// CHECK-YAML1: ReplacementText: "\n#endif\n" + +// CHECK-YAML1: Message: header is missing header guard +// CHECK-YAML1: FilePath: '{{.*header-guard.include.other.}}missing.hpp' +// CHECK-YAML1: ReplacementText: "#ifndef OTHER_MISSING_HPP\n#define OTHER_MISSING_HPP\n\n" +// CHECK-YAML1: ReplacementText: "\n#endif\n" + +// CHECK-YAML1: Message: header guard does not follow preferred style +// CHECK-YAML1: FilePath: '{{.*header-guard.include.other.}}wrong.hpp' +// CHECK-YAML1: ReplacementText: OTHER_WRONG_HPP + +// CHECK-YAML1: Message: header guard does not follow preferred style +// CHECK-YAML1: FilePath: '{{.*header-guard.include.}}wrong.hpp' +// CHECK-YAML1: ReplacementText: WRONG_HPP + + +// --------------------------------------- +// TEST 2: Set option HeaderDirs=other +// --------------------------------------- +// RUN: %check_clang_tidy %s misc-header-guard %t -export-fixes=%t.2.yaml --header-filter=.* \ +// RUN: --config='{CheckOptions: { \ +// RUN: misc-header-guard.HeaderDirs: other, \ +// RUN: }}' -- -I%S > %t.2.msg 2>&1 +// RUN: FileCheck -input-file=%t.2.msg -check-prefix=CHECK-MESSAGES2 %s +// RUN: FileCheck -input-file=%t.2.yaml -check-prefix=CHECK-YAML2 %s + +// CHECK-MESSAGES2: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES2: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES2: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES2: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES2: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES2: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] + +// CHECK-YAML2: Message: header guard does not follow preferred style +// CHECK-YAML2: FilePath: '{{.*header-guard.include.}}correct.hpp' +// CHECK-YAML2: ReplacementText: {{.*}}CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MISC_HEADER_GUARD_INCLUDE_CORRECT_HPP + +// CHECK-YAML2: Message: header is missing header guard +// CHECK-YAML2: FilePath: '{{.*header-guard.include.}}missing.hpp' +// CHECK-YAML2: ReplacementText: "#ifndef {{.*}}CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MISC_HEADER_GUARD_INCLUDE_MISSING_HPP\n#define {{.*}}CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MISC_HEADER_GUARD_INCLUDE_MISSING_HPP\n\n" +// CHECK-YAML2: ReplacementText: "\n#endif\n" + +// CHECK-YAML2: Message: header guard does not follow preferred style +// CHECK-YAML2: FilePath: '{{.*header-guard.include.other.}}correct.hpp' +// CHECK-YAML2: ReplacementText: CORRECT_HPP + +// CHECK-YAML2: Message: header is missing header guard +// CHECK-YAML2: FilePath: '{{.*header-guard.include.other.}}missing.hpp' +// CHECK-YAML2: ReplacementText: "#ifndef MISSING_HPP\n#define MISSING_HPP\n\n" +// CHECK-YAML2: ReplacementText: "\n#endif\n" + +// CHECK-YAML2: Message: header guard does not follow preferred style +// CHECK-YAML2: FilePath: '{{.*header-guard.include.other.}}wrong.hpp' +// CHECK-YAML2: ReplacementText: WRONG_HPP + +// CHECK-YAML2: Message: header guard does not follow preferred style +// CHECK-YAML2: FilePath: '{{.*header-guard.include.}}wrong.hpp' +// CHECK-YAML2: ReplacementText: {{.*}}CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MISC_HEADER_GUARD_INCLUDE_WRONG_HPP + + +// --------------------------------------- +// TEST 3: Set option HeaderDirs=other;include +// --------------------------------------- +// RUN: %check_clang_tidy %s misc-header-guard %t -export-fixes=%t.3.yaml --header-filter=.* \ +// RUN: --config='{CheckOptions: { \ +// RUN: misc-header-guard.HeaderDirs: other;include, \ +// RUN: }}' -- -I%S > %t.3.msg 2>&1 +// RUN: FileCheck -input-file=%t.3.msg -check-prefix=CHECK-MESSAGES3 %s +// RUN: FileCheck -input-file=%t.3.yaml -check-prefix=CHECK-YAML3 %s + +// CHECK-MESSAGES3: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES3: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES3: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES3: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES3: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES3: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] + +// CHECK-YAML3: Message: header is missing header guard +// CHECK-YAML3: FilePath: '{{.*header-guard.include.}}missing.hpp' +// CHECK-YAML3: ReplacementText: "#ifndef MISSING_HPP\n#define MISSING_HPP\n\n" +// CHECK-YAML3: ReplacementText: "\n#endif\n" + +// CHECK-YAML3: Message: header guard does not follow preferred style +// CHECK-YAML3: FilePath: '{{.*header-guard.include.other.}}correct.hpp' +// CHECK-YAML3: ReplacementText: CORRECT_HPP + +// CHECK-YAML3: Message: header is missing header guard +// CHECK-YAML3: FilePath: '{{.*header-guard.include.other.}}missing.hpp' +// CHECK-YAML3: ReplacementText: "#ifndef MISSING_HPP\n#define MISSING_HPP\n\n" +// CHECK-YAML3: ReplacementText: "\n#endif\n" + +// CHECK-YAML3: Message: header guard does not follow preferred style +// CHECK-YAML3: FilePath: '{{.*header-guard.include.other.}}wrong.hpp' +// CHECK-YAML3: ReplacementText: WRONG_HPP + +// CHECK-YAML3: Message: header guard does not follow preferred style +// CHECK-YAML3: FilePath: '{{.*header-guard.include.}}wrong.hpp' +// CHECK-YAML3: ReplacementText: WRONG_HPP + + +// ------------------------------------------------------------------- +// TEST 4: Set option HeaderDirs=other;include and Prefix=SOME_PREFIX_ +// ------------------------------------------------------------------- +// RUN: %check_clang_tidy %s misc-header-guard %t -export-fixes=%t.4.yaml --header-filter=.* \ +// RUN: --config='{CheckOptions: { \ +// RUN: misc-header-guard.Prefix: SOME_PREFIX_, \ +// RUN: }}' -- -I%S > %t.4.msg 2>&1 +// RUN: FileCheck -input-file=%t.4.msg -check-prefix=CHECK-MESSAGES4 %s +// RUN: FileCheck -input-file=%t.4.yaml -check-prefix=CHECK-YAML4 %s + +// CHECK-MESSAGES4: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES4: correct.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES4: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES4: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES4: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES4: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] + +// CHECK-YAML4: Message: header guard does not follow preferred style +// CHECK-YAML4: FilePath: '{{.*header-guard.include.}}correct.hpp' +// CHECK-YAML4: ReplacementText: SOME_PREFIX_CORRECT_HPP + +// CHECK-YAML4: Message: header is missing header guard +// CHECK-YAML4: FilePath: '{{.*header-guard.include.}}missing.hpp' +// CHECK-YAML4: ReplacementText: "#ifndef SOME_PREFIX_MISSING_HPP\n#define SOME_PREFIX_MISSING_HPP\n\n" +// CHECK-YAML4: ReplacementText: "\n#endif\n" + +// CHECK-YAML4: Message: header guard does not follow preferred style +// CHECK-YAML4: FilePath: '{{.*header-guard.include.other.}}correct.hpp' +// CHECK-YAML4: ReplacementText: SOME_PREFIX_OTHER_CORRECT_HPP + +// CHECK-YAML4: Message: header is missing header guard +// CHECK-YAML4: FilePath: '{{.*header-guard.include.other.}}missing.hpp' +// CHECK-YAML4: ReplacementText: "#ifndef SOME_PREFIX_OTHER_MISSING_HPP\n#define SOME_PREFIX_OTHER_MISSING_HPP\n\n" +// CHECK-YAML4: ReplacementText: "\n#endif\n" + +// CHECK-YAML4: Message: header guard does not follow preferred style +// CHECK-YAML4: FilePath: '{{.*header-guard.include.other.}}wrong.hpp' +// CHECK-YAML4: ReplacementText: SOME_PREFIX_OTHER_WRONG_HPP + +// CHECK-YAML4: Message: header guard does not follow preferred style +// CHECK-YAML4: FilePath: '{{.*header-guard.include.}}wrong.hpp' +// CHECK-YAML4: ReplacementText: SOME_PREFIX_WRONG_HPP + + + +// ------------------------------------------------------------------- +// TEST 5: Set option HeaderDirs=include/other;include and EndifComment=true +// ------------------------------------------------------------------- +// RUN: %check_clang_tidy %s misc-header-guard %t -export-fixes=%t.5.yaml --header-filter=.* \ +// RUN: --config='{CheckOptions: { \ +// RUN: misc-header-guard.EndifComment: true, \ +// RUN: }}' -- -I%S > %t.5.msg 2>&1 +// RUN: FileCheck -input-file=%t.5.msg -check-prefix=CHECK-MESSAGES5 %s +// RUN: FileCheck -input-file=%t.5.yaml -check-prefix=CHECK-YAML5 %s + +// CHECK-MESSAGES5: correct.hpp:3:2: warning: #endif for a header guard should reference the guard macro in a comment [misc-header-guard] +// CHECK-MESSAGES5: missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES5: other{{.}}missing.hpp:1:1: warning: header is missing header guard [misc-header-guard] +// CHECK-MESSAGES5: other{{.}}wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] +// CHECK-MESSAGES5: wrong.hpp:1:9: warning: header guard does not follow preferred style [misc-header-guard] + +// CHECK-YAML5: Message: '#endif for a header guard should reference the guard macro in a comment' +// CHECK-YAML5: FilePath: '{{.*header-guard.include.}}correct.hpp' +// CHECK-YAML5: ReplacementText: 'endif // CORRECT_HPP' + +// CHECK-YAML5: Message: header is missing header guard +// CHECK-YAML5: FilePath: '{{.*header-guard.include.}}missing.hpp' +// CHECK-YAML5: ReplacementText: "#ifndef MISSING_HPP\n#define MISSING_HPP\n\n" +// CHECK-YAML5: ReplacementText: "\n#endif // MISSING_HPP\n" + +// CHECK-YAML5: Message: '#endif for a header guard should reference the guard macro in a comment' +// CHECK-YAML5: FilePath: '{{.*header-guard.include.other.}}correct.hpp' +// CHECK-YAML5: ReplacementText: 'endif // OTHER_CORRECT_HPP' + +// CHECK-YAML5: Message: header is missing header guard +// CHECK-YAML5: FilePath: '{{.*header-guard.include.other.}}missing.hpp' +// CHECK-YAML5: ReplacementText: "#ifndef OTHER_MISSING_HPP\n#define OTHER_MISSING_HPP\n\n" +// CHECK-YAML5: ReplacementText: "\n#endif // OTHER_MISSING_HPP\n" + +// CHECK-YAML5: Message: header guard does not follow preferred style +// CHECK-YAML5: FilePath: '{{.*header-guard.include.other.}}wrong.hpp' +// CHECK-YAML5: ReplacementText: OTHER_WRONG_HPP + +// CHECK-YAML5: Message: header guard does not follow preferred style +// CHECK-YAML5: FilePath: '{{.*header-guard.include.}}wrong.hpp' +// CHECK-YAML5: ReplacementText: WRONG_HPP diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/correct.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/correct.hpp new file mode 100644 index 0000000000000..7eeead99748e9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/correct.hpp @@ -0,0 +1,7 @@ +#ifndef CORRECT_HPP +#define CORRECT_HPP +#endif + +// RUN: %check_clang_tidy %s misc-header-guard correct -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: warning: code/includes outside of area guarded by header guard; consider moving it [misc-header-guard] diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/missing.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/missing.hpp new file mode 100644 index 0000000000000..4a5a3a495a2b1 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/missing.hpp @@ -0,0 +1,3 @@ +// RUN: %check_clang_tidy %s misc-header-guard missing -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: :1:1: warning: header is missing header guard [misc-header-guard] diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/correct.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/correct.hpp new file mode 100644 index 0000000000000..97ea9db05cd41 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/correct.hpp @@ -0,0 +1,7 @@ +#ifndef OTHER_CORRECT_HPP +#define OTHER_CORRECT_HPP +#endif + +// RUN: %check_clang_tidy %s misc-header-guard correct -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: warning: code/includes outside of area guarded by header guard; consider moving it [misc-header-guard] diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/missing.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/missing.hpp new file mode 100644 index 0000000000000..4a5a3a495a2b1 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/missing.hpp @@ -0,0 +1,3 @@ +// RUN: %check_clang_tidy %s misc-header-guard missing -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: :1:1: warning: header is missing header guard [misc-header-guard] diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/wrong.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/wrong.hpp new file mode 100644 index 0000000000000..a9afba832a10a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/other/wrong.hpp @@ -0,0 +1,7 @@ +#ifndef SOME_WRONG_HEADER_GUARD_HPP +#define SOME_WRONG_HEADER_GUARD_HPP +#endif + +// RUN: %check_clang_tidy %s misc-header-guard wrong -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: warning: header guard does not follow preferred style [misc-header-guard] diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/pragma-once.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/pragma-once.hpp new file mode 100644 index 0000000000000..fa174cfe9a3a9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/pragma-once.hpp @@ -0,0 +1,10 @@ +#pragma once + +// RUN: %check_clang_tidy %s misc-header-guard pragma-once -export-fixes=%t.1.yaml > %t.1.msg 2>&1 +// RUN: FileCheck -input-file=%t.1.msg -check-prefix=CHECK-MSG1 %s +// CHECK-MSG1: pragma-once.hpp:1:1: warning: 'pragma once' is not allowed; use include guards instead [misc-header-guard] + +// RUN: %check_clang_tidy %s misc-header-guard pragma-once \ +// RUN: --config='{CheckOptions: { \ +// RUN: misc-header-guard.AllowPragmaOnce: true, \ +// RUN: }}' > %t.2.msg 2>&1 diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/wrong.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/wrong.hpp new file mode 100644 index 0000000000000..e5ca81da6a680 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/header-guard/include/wrong.hpp @@ -0,0 +1,7 @@ +#ifndef HERE_IS_SOMETHING_WRONG_HPP +#define HERE_IS_SOMETHING_WRONG_HPP +#endif + +// RUN: %check_clang_tidy %s misc-header-guard wrong -export-fixes=%t.yaml > %t.msg 2>&1 +// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MSG %s +// CHECK-MSG: warning: header guard does not follow preferred style [misc-header-guard] >From 9e32c3d91e2d0f8edb271e3ecebafcc3016bec1f Mon Sep 17 00:00:00 2001 From: Thorsten Klein <[email protected]> Date: Thu, 22 Jan 2026 08:15:20 +0100 Subject: [PATCH 2/2] Disable clang-tidy misc-header-guard This does not apply well to LLVM which intentionally have own llvm-header-guard check. --- .clang-tidy | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-tidy b/.clang-tidy index 2cda1b81de808..bb404c291cd13 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,6 +4,7 @@ Checks: > clang-diagnostic-*, llvm-*, misc-*, + -misc-header-guard, -misc-const-correctness, -misc-include-cleaner, -misc-no-recursion, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
