PiotrZSL created this revision. Herald added subscribers: carlosgalvezp, xazax.hun. Herald added a reviewer: njames93. Herald added a project: All. PiotrZSL updated this revision to Diff 500591. PiotrZSL added a comment. Eugene.Zelenko added reviewers: aaron.ballman, carlosgalvezp. Eugene.Zelenko added a project: clang-tools-extra. PiotrZSL updated this revision to Diff 501040. PiotrZSL published this revision for review. Herald added a subscriber: cfe-commits.
Typo fix. PiotrZSL added a comment. Fixes PiotrZSL added a comment. Findings on llvm: clang-tools-extra/unittests/clang-doc/ClangDocTest.h:12:10: warning: direct self-inclusion of header file 'ClangDocTest.h' [misc-header-include-cycle] libc/src/__support/FPUtil/x86_64/LongDoubleBits.h:21:10: warning: circular header file dependency detected while including 'FPBits.h', please check the include path: 'FPBits.h' -> 'LongDoubleBits.h' -> 'FPBits.h' [misc-header-include-cycle] lldb/include/lldb/API/SBAddress.h:13:10: warning: circular header file dependency detected while including 'SBModule.h', please check the include path: 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' -> 'SBAddress.h' -> 'SBModule.h' [misc-header-include-cycle] lldb/include/lldb/API/SBBlock.h:14:10: warning: circular header file dependency detected while including 'SBTarget.h', please check the include path: 'SBTarget.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' [misc-header-include-cycle] lldb/include/lldb/API/SBFunction.h:12:10: warning: circular header file dependency detected while including 'SBAddress.h', please check the include path: 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBFunction.h' -> 'SBAddress.h' [misc-header-include-cycle] lldb/include/lldb/API/SBLineEntry.h:12:10: warning: circular header file dependency detected while including 'SBAddress.h', please check the include path: 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBLineEntry.h' -> 'SBAddress.h' [misc-header-include-cycle] lldb/include/lldb/API/SBModule.h:15:10: warning: circular header file dependency detected while including 'SBSymbolContext.h', please check the include path: 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbol.h:12:10: warning: circular header file dependency detected while including 'SBAddress.h', please check the include path: 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBSymbol.h' -> 'SBAddress.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbol.h:15:10: warning: circular header file dependency detected while including 'SBTarget.h', please check the include path: 'SBTarget.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBSymbol.h' -> 'SBTarget.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContext.h:12:10: warning: circular header file dependency detected while including 'SBBlock.h', please check the include path: 'SBBlock.h' -> 'SBTarget.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBBlock.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContext.h:15:10: warning: circular header file dependency detected while including 'SBFunction.h', please check the include path: 'SBFunction.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBFunction.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContext.h:16:10: warning: circular header file dependency detected while including 'SBLineEntry.h', please check the include path: 'SBLineEntry.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBLineEntry.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContext.h:17:10: warning: circular header file dependency detected while including 'SBModule.h', please check the include path: 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBModule.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContext.h:18:10: warning: circular header file dependency detected while including 'SBSymbol.h', please check the include path: 'SBSymbol.h' -> 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBSymbol.h' [misc-header-include-cycle] lldb/include/lldb/API/SBSymbolContextList.h:13:10: warning: circular header file dependency detected while including 'SBSymbolContext.h', please check the include path: 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' -> 'SBSymbolContextList.h' -> 'SBSymbolContext.h' [misc-header-include-cycle] lldb/include/lldb/API/SBTarget.h:12:10: warning: circular header file dependency detected while including 'SBAddress.h', please check the include path: 'SBAddress.h' -> 'SBModule.h' -> 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' -> 'SBAddress.h' [misc-header-include-cycle] lldb/include/lldb/API/SBTarget.h:20:10: warning: circular header file dependency detected while including 'SBSymbolContextList.h', please check the include path: 'SBSymbolContextList.h' -> 'SBSymbolContext.h' -> 'SBBlock.h' -> 'SBTarget.h' -> 'SBSymbolContextList.h' [misc-header-include-cycle] lldb/include/lldb/lldb-private-types.h:14:10: warning: circular header file dependency detected while including 'lldb-private.h', please check the include path: 'lldb-private.h' -> 'lldb-private-types.h' -> 'lldb-private.h' [misc-header-include-cycle] lldb/include/lldb/lldb-private.h:16:10: warning: circular header file dependency detected while including 'lldb-private-types.h', please check the include path: 'lldb-private-types.h' -> 'lldb-private.h' -> 'lldb-private-types.h' [misc-header-include-cycle] lldb/source/Plugins/Process/scripted/ScriptedProcess.h:17:10: warning: circular header file dependency detected while including 'ScriptedThread.h', please check the include path: 'ScriptedThread.h' -> 'ScriptedProcess.h' -> 'ScriptedThread.h' [misc-header-include-cycle] lldb/source/Plugins/Process/scripted/ScriptedThread.h:14:10: warning: circular header file dependency detected while including 'ScriptedProcess.h', please check the include path: 'ScriptedProcess.h' -> 'ScriptedThread.h' -> 'ScriptedProcess.h' [misc-header-include-cycle] third-party/unittest/googletest/include/gtest/gtest_pred_impl.h:43:10: warning: circular header file dependency detected while including 'gtest.h', please check the include path: 'gtest.h' -> 'gtest_pred_impl.h' -> 'gtest.h' [misc-header-include-cycle] PiotrZSL added a comment. Ready for review Check detects cyclic #include dependencies between user-defined headers. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D144828 Files: clang-tools-extra/clang-tidy/misc/CMakeLists.txt clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.cpp clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.h clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/list.rst clang-tools-extra/docs/clang-tidy/checks/misc/header-include-cycle.rst clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first-d.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth-d.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second-d.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self-d.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third-d.hpp clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third.hpp clang-tools-extra/test/clang-tidy/checkers/misc/header-include-cycle.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/misc/header-include-cycle.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/header-include-cycle.cpp @@ -0,0 +1,17 @@ +// RUN: rm -rf %T/misc-header-include-cycle-headers +// RUN: mkdir %T/misc-header-include-cycle-headers +// RUN: cp -r %S/Inputs/header-include-cycle* %T/misc-header-include-cycle-headers/ +// RUN: clang-tidy %s -checks='-*,misc-header-include-cycle' -header-filter=.* -- -I%T/misc-header-include-cycle-headers | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:" +// RUN: rm -rf %T/misc-header-include-cycle-headers + +#include <header-include-cycle.first-d.hpp> +// CHECK-MESSAGES: header-include-cycle.fourth-d.hpp:3:10: warning: circular header file dependency detected while including 'header-include-cycle.first-d.hpp', please check the include path: 'header-include-cycle.first-d.hpp' -> 'header-include-cycle.second-d.hpp' -> 'header-include-cycle.third-d.hpp' -> 'header-include-cycle.fourth-d.hpp' -> 'header-include-cycle.first-d.hpp' [misc-header-include-cycle] + +#include <header-include-cycle.first.hpp> +// CHECK-MESSAGES: header-include-cycle.fourth.hpp:2:10: warning: circular header file dependency detected while including 'header-include-cycle.first.hpp', please check the include path: 'header-include-cycle.first.hpp' -> 'header-include-cycle.second.hpp' -> 'header-include-cycle.third.hpp' -> 'header-include-cycle.fourth.hpp' -> 'header-include-cycle.first.hpp' [misc-header-include-cycle] + +#include <header-include-cycle.self-d.hpp> +// CHECK-MESSAGES: header-include-cycle.self-d.hpp:3:10: warning: direct self-inclusion of header file 'header-include-cycle.self-d.hpp' [misc-header-include-cycle] + +#include <header-include-cycle.self.hpp> +// CHECK-MESSAGES: header-include-cycle.self.hpp:2:10: warning: direct self-inclusion of header file 'header-include-cycle.self.hpp' [misc-header-include-cycle] Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "header-include-cycle.fourth.hpp" Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third-d.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.third-d.hpp @@ -0,0 +1,4 @@ +#ifndef THIRD +#define THIRD +#include "header-include-cycle.fourth-d.hpp" +#endif Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "header-include-cycle.self.hpp" Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self-d.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.self-d.hpp @@ -0,0 +1,4 @@ +#ifndef SELF +#define SELF +#include "header-include-cycle.self-d.hpp" +#endif Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "header-include-cycle.third.hpp" Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second-d.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.second-d.hpp @@ -0,0 +1,4 @@ +#ifndef SECOND +#define SECOND +#include "header-include-cycle.third-d.hpp" +#endif Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "header-include-cycle.first.hpp" Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth-d.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.fourth-d.hpp @@ -0,0 +1,4 @@ +#ifndef FOURTH +#define FOURTH +#include "header-include-cycle.first-d.hpp" +#endif Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "header-include-cycle.second.hpp" Index: clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first-d.hpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/header-include-cycle.first-d.hpp @@ -0,0 +1,4 @@ +#ifndef FIRST +#define FIRST +#include "header-include-cycle.second-d.hpp" +#endif Index: clang-tools-extra/docs/clang-tidy/checks/misc/header-include-cycle.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/misc/header-include-cycle.rst @@ -0,0 +1,63 @@ +.. title:: clang-tidy - misc-header-include-cycle + +misc-header-include-cycle +========================= + +Check detects cyclic ``#include`` dependencies between user-defined headers. + +.. code-block:: c++ + + // Header A.hpp + #pragma once + #include "B.hpp" + + // Header B.hpp + #pragma once + #include "C.hpp" + + // Header C.hpp + #pragma once + #include "A.hpp" + + // Include chain: A->B->C->A + +Header files are a crucial part of many C++ programs, as they provide a way to +organize declarations and definitions that are shared across multiple source +files. However, header files can also create problems when they become entangled +in complex dependency cycles. Such cycles can cause issues with compilation +times, unnecessary rebuilds, and make it harder to understand the overall +structure of the code. + +To address these issues, this check has been developed. This check is designed +to detect cyclic dependencies between header files, also known as +"include cycles". An include cycle occurs when a header file `A` includes a +header file `B`, and header file `B` (or any later included header file in the +chain) includes back header file `A`, leading to a circular dependency cycle. + +This check operates at the preprocessor level and analyzes user-defined headers +and their dependencies. It focuses specifically on detecting include cycles, +and ignores other types or function dependencies. This allows it to provide a +specialized analysis that is focused on identifying and preventing issues +related to header file organization. + +The benefits of using this check are numerous. By detecting include cycles early +in the development process, developers can identify and resolve these issues +before they become more difficult and time-consuming to fix. This can lead to +faster compile times, improved code quality, and a more maintainable codebase +overall. Additionally, by ensuring that header files are organized in a way that +avoids cyclic dependencies, developers can make their code easier to understand +and modify over time. + +It's worth noting that this tool only analyzes user-defined headers and their +dependencies, excluding system includes such as standard library headers and +third-party library headers. System includes are usually well-designed and free +of include cycles, and ignoring them helps to focus on potential issues within +the project's own codebase. This limitation doesn't diminish the tool's ability +to detect ``#include`` cycles within the analyzed code. As with any tool, +developers should use their judgment when evaluating the warnings produced by +the check and be prepared to make exceptions or modifications to their code as +needed. + +This check has no options to configure. + +**Catch header cycles before they catch you - Try this Clang-tidy check today!** 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 @@ -249,6 +249,7 @@ `misc-confusable-identifiers <misc/confusable-identifiers.html>`_, `misc-const-correctness <misc/const-correctness.html>`_, "Yes" `misc-definitions-in-headers <misc/definitions-in-headers.html>`_, "Yes" + `misc-header-include-cycle <misc/header-include-cycle.html>`_, `misc-misleading-bidirectional <misc/misleading-bidirectional.html>`_, `misc-misleading-identifier <misc/misleading-identifier.html>`_, `misc-misplaced-const <misc/misplaced-const.html>`_, Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -120,6 +120,11 @@ Checks that all implicit and explicit inline functions in header files are tagged with the ``LIBC_INLINE`` macro. +- New :doc:`misc-header-include-cycle + <clang-tidy/checks/misc/header-include-cycle>` check. + + Check detects cyclic ``#include`` dependencies between user-defined headers. + New check aliases ^^^^^^^^^^^^^^^^^ Index: clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -12,6 +12,7 @@ #include "ConfusableIdentifierCheck.h" #include "ConstCorrectnessCheck.h" #include "DefinitionsInHeadersCheck.h" +#include "HeaderIncludeCycleCheck.h" #include "MisleadingBidirectional.h" #include "MisleadingIdentifier.h" #include "MisplacedConstCheck.h" @@ -41,6 +42,8 @@ "misc-const-correctness"); CheckFactories.registerCheck<DefinitionsInHeadersCheck>( "misc-definitions-in-headers"); + CheckFactories.registerCheck<HeaderIncludeCycleCheck>( + "misc-header-include-cycle"); CheckFactories.registerCheck<MisleadingBidirectionalCheck>( "misc-misleading-bidirectional"); CheckFactories.registerCheck<MisleadingIdentifierCheck>( Index: clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.h @@ -0,0 +1,30 @@ +//===--- HeaderIncludeCycleCheck.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_MISC_HEADERINCLUDECYCLECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_HEADERINCLUDECYCLECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::misc { + +/// Check detects cyclic #include dependencies between user-defined headers. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc/header-include-cycle.html +class HeaderIncludeCycleCheck : public ClangTidyCheck { +public: + HeaderIncludeCycleCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_HEADERINCLUDECYCLECHECK_H Index: clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.cpp @@ -0,0 +1,146 @@ +//===--- HeaderIncludeCycleCheck.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 "HeaderIncludeCycleCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallVector.h" +#include <algorithm> +#include <string> + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +namespace { + +class CyclicDependencyCallbacks : public PPCallbacks { +public: + CyclicDependencyCallbacks(HeaderIncludeCycleCheck &Check, + const SourceManager &SM) + : Check(Check), SM(SM) { + Files.emplace_back(SM.getMainFileID()); + } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override { + if (FileType != clang::SrcMgr::C_User) + return; + + if (Reason != EnterFile && Reason != ExitFile) + return; + + FileID Id = SM.getFileID(Loc); + if (Id.isInvalid()) + return; + + if (Reason == EnterFile) { + if (Files.empty() || Files.back() != Id) { + Files.emplace_back(Id); + } + } else if ((Files.size() > 1U) && (Files.back() == PrevFID) && + (Files[Files.size() - 2U] == Id)) + Files.pop_back(); + } + + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override { + if (FileType != clang::SrcMgr::C_User) + return; + + checkForDoubleInclude(SkippedFile, FilenameTok.getLocation()); + } + + void InclusionDirective(SourceLocation, const Token &, StringRef, bool, + CharSourceRange FilenameRange, + OptionalFileEntryRef File, StringRef, StringRef, + const Module *, + SrcMgr::CharacteristicKind FileType) override { + if (FileType != clang::SrcMgr::C_User) + return; + + if (File) + checkForDoubleInclude(*File, FilenameRange.getBegin()); + } + + void EndOfMainFile() override { assert(Files.size() == 1U); } + + void checkForDoubleInclude(const FileEntryRef &File, SourceLocation Loc) { + FileID Id = SM.translateFile(File); + if (Id.isInvalid()) + return; + + auto It = std::find(Files.begin(), Files.end(), Id); + if (It == Files.end()) + return; + + SmallVector<llvm::StringRef, 32U> IncludePath; + + while (It != Files.end()) { + std::optional<llvm::StringRef> FilePath = + SM.getNonBuiltinFilenameForID(*It); + ++It; + if (!FilePath) + continue; + IncludePath.emplace_back(getFileName(*FilePath)); + } + + std::optional<llvm::StringRef> FilePath = SM.getNonBuiltinFilenameForID(Id); + if (!FilePath) + return; + + IncludePath.emplace_back(getFileName(*FilePath)); + reportDiagnostic(Loc, IncludePath); + } + + void reportDiagnostic(const SourceLocation &Loc, + const SmallVector<llvm::StringRef, 32U> &History) { + if (History.size() == 2U) { + Check.diag(Loc, "direct self-inclusion of header file '%0'") + << History.back(); + return; + } + + std::string IncludePath; + for (const llvm::StringRef &Name : History) { + IncludePath += '\''; + IncludePath += Name; + IncludePath += "' -> "; + } + + Check.diag(Loc, "circular header file dependency detected while including " + "'%0', please check the include path: %1") + << History.back() << llvm::StringRef(IncludePath).rsplit(" -> ").first; + } + + static llvm::StringRef getFileName(llvm::StringRef FilePath) { + llvm::StringRef FileName = FilePath; + if (FileName.contains('/')) + FileName = FileName.rsplit('/').second; + if (FileName.contains('\\')) + FileName = FileName.rsplit('\\').second; + return FileName; + } + +private: + SmallVector<FileID, 1024U> Files; + HeaderIncludeCycleCheck &Check; + const SourceManager &SM; +}; + +} // namespace + +void HeaderIncludeCycleCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + PP->addPPCallbacks(std::make_unique<CyclicDependencyCallbacks>(*this, SM)); +} + +} // namespace clang::tidy::misc Index: clang-tools-extra/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -19,6 +19,7 @@ ConstCorrectnessCheck.cpp DefinitionsInHeadersCheck.cpp ConfusableIdentifierCheck.cpp + HeaderIncludeCycleCheck.cpp MiscTidyModule.cpp MisleadingBidirectional.cpp MisleadingIdentifier.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits