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);
43 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
44 SrcMgr::CharacteristicKind FileType,
45 FileID PrevFID)
override {
46 if (FileType != clang::SrcMgr::C_User)
49 if (Reason != EnterFile && Reason != ExitFile)
52 const FileID Id = SM.getFileID(Loc);
56 const FileEntry *NewFile = SM.getFileEntryForID(Id);
57 const FileEntry *PrevFile = SM.getFileEntryForID(PrevFID);
59 if (Reason == ExitFile) {
60 if ((Files.size() > 1U) && (Files.back().File == PrevFile) &&
61 (Files[Files.size() - 2U].File == NewFile))
66 if (!Files.empty() && Files.back().File == NewFile)
69 const std::optional<StringRef> FilePath = SM.getNonBuiltinFilenameForID(Id);
70 const StringRef FileName =
71 FilePath ? llvm::sys::path::filename(*FilePath) : StringRef();
72 Files.push_back({NewFile, FileName, std::exchange(NextToEnter, {})});
75 void InclusionDirective(SourceLocation,
const Token &, StringRef FilePath,
76 bool, CharSourceRange Range,
77 OptionalFileEntryRef File, StringRef, StringRef,
79 SrcMgr::CharacteristicKind FileType)
override {
80 if (FileType != clang::SrcMgr::C_User)
83 NextToEnter = Range.getBegin();
88 checkForDoubleInclude(&
File->getFileEntry(),
89 llvm::sys::path::filename(FilePath),
93 void checkForDoubleInclude(
const FileEntry *File, StringRef FileName,
96 llvm::find_if(llvm::reverse(Files),
97 [&](
const Include &Entry) {
return Entry.File ==
File; });
98 if (It == Files.rend())
101 const StringRef FilePath =
File->tryGetRealPathName();
102 if (FilePath.empty() || isFileIgnored(FilePath))
105 if (It == Files.rbegin()) {
106 Check.diag(Loc,
"direct self-inclusion of header file '%0'") << FileName;
110 Check.diag(Loc,
"circular header file dependency detected while including "
111 "'%0', please check the include path")
114 const bool IsIncludePathValid =
115 std::all_of(Files.rbegin(), It + 1, [](
const Include &Elem) {
116 return !Elem.Name.empty() && Elem.Loc.isValid();
118 if (!IsIncludePathValid)
121 for (
const Include &I : llvm::make_range(Files.rbegin(), It + 1))
122 Check.diag(I.Loc,
"'%0' included from here", DiagnosticIDs::Note)
126 bool isFileIgnored(StringRef FileName)
const {
127 return llvm::any_of(IgnoredFilesRegexes, [&](
const llvm::Regex &It) {
128 return It.match(FileName);
133 void EndOfMainFile()
override {
134 if (!Files.empty() &&
135 Files.back().File == SM.getFileEntryForID(SM.getMainFileID()))
138 assert(Files.empty());
143 std::vector<Include> Files;
144 SourceLocation NextToEnter;
145 HeaderIncludeCycleCheck &Check;
146 const SourceManager &SM;
147 std::vector<llvm::Regex> IgnoredFilesRegexes;
155 IgnoredFilesList(
utils::options::parseStringList(
156 Options.get(
"IgnoredFilesList",
""))) {}
159 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
161 std::make_unique<CyclicDependencyCallbacks>(*
this, SM, IgnoredFilesList));
165 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