11#include "clang/AST/ASTContext.h"
12#include "clang/Lex/PPCallbacks.h"
13#include "clang/Lex/Preprocessor.h"
14#include "llvm/Support/Regex.h"
26 const FileEntry *File;
31class CyclicDependencyCallbacks :
public PPCallbacks {
33 CyclicDependencyCallbacks(HeaderIncludeCycleCheck &Check,
34 const SourceManager &SM,
35 const std::vector<StringRef> &IgnoredFilesList)
36 : Check(Check), SM(SM) {
37 IgnoredFilesRegexes.reserve(IgnoredFilesList.size());
38 for (
const StringRef &It : IgnoredFilesList) {
40 IgnoredFilesRegexes.emplace_back(It);
44 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
45 SrcMgr::CharacteristicKind FileType,
46 FileID PrevFID)
override {
47 if (FileType != clang::SrcMgr::C_User)
50 if (Reason != EnterFile && Reason != ExitFile)
53 const FileID Id = SM.getFileID(Loc);
57 const FileEntry *NewFile = SM.getFileEntryForID(Id);
58 const FileEntry *PrevFile = SM.getFileEntryForID(PrevFID);
60 if (Reason == ExitFile) {
61 if ((Files.size() > 1U) && (Files.back().File == PrevFile) &&
62 (Files[Files.size() - 2U].File == NewFile))
67 if (!Files.empty() && Files.back().File == NewFile)
70 const std::optional<StringRef> FilePath = SM.getNonBuiltinFilenameForID(Id);
71 const StringRef FileName =
72 FilePath ? llvm::sys::path::filename(*FilePath) : StringRef();
73 Files.push_back({NewFile, FileName, std::exchange(NextToEnter, {})});
76 void InclusionDirective(SourceLocation,
const Token &, StringRef FilePath,
77 bool, CharSourceRange Range,
78 OptionalFileEntryRef File, StringRef, StringRef,
80 SrcMgr::CharacteristicKind FileType)
override {
81 if (FileType != clang::SrcMgr::C_User)
84 NextToEnter = Range.getBegin();
89 checkForDoubleInclude(&
File->getFileEntry(),
90 llvm::sys::path::filename(FilePath),
94 void checkForDoubleInclude(
const FileEntry *File, StringRef FileName,
97 llvm::find_if(llvm::reverse(Files),
98 [&](
const Include &Entry) {
return Entry.File ==
File; });
99 if (It == Files.rend())
102 const StringRef FilePath =
File->tryGetRealPathName();
103 if (FilePath.empty() || isFileIgnored(FilePath))
106 if (It == Files.rbegin()) {
107 Check.diag(Loc,
"direct self-inclusion of header file '%0'") << FileName;
111 Check.diag(Loc,
"circular header file dependency detected while including "
112 "'%0', please check the include path")
115 const bool IsIncludePathValid =
116 std::all_of(Files.rbegin(), It + 1, [](
const Include &Elem) {
117 return !Elem.Name.empty() && Elem.Loc.isValid();
119 if (!IsIncludePathValid)
122 for (
const Include &I : llvm::make_range(Files.rbegin(), It + 1))
123 Check.diag(I.Loc,
"'%0' included from here", DiagnosticIDs::Note)
127 bool isFileIgnored(StringRef FileName)
const {
128 return llvm::any_of(IgnoredFilesRegexes, [&](
const llvm::Regex &It) {
129 return It.match(FileName);
134 void EndOfMainFile()
override {
135 if (!Files.empty() &&
136 Files.back().File == SM.getFileEntryForID(SM.getMainFileID()))
139 assert(Files.empty());
144 std::vector<Include> Files;
145 SourceLocation NextToEnter;
146 HeaderIncludeCycleCheck &Check;
147 const SourceManager &SM;
148 std::vector<llvm::Regex> IgnoredFilesRegexes;
156 IgnoredFilesList(
utils::options::parseStringList(
157 Options.get(
"IgnoredFilesList",
""))) {}
160 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
162 std::make_unique<CyclicDependencyCallbacks>(*
this, SM, IgnoredFilesList));
166 Options.store(Opts,
"IgnoredFilesList",
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap