10#include "../utils/OptionsUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/PPCallbacks.h"
14#include "clang/Lex/Preprocessor.h"
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/Support/Regex.h"
34class CyclicDependencyCallbacks :
public PPCallbacks {
36 CyclicDependencyCallbacks(HeaderIncludeCycleCheck &Check,
37 const SourceManager &SM,
38 const std::vector<StringRef> &IgnoredFilesList)
39 : Check(Check), SM(SM) {
40 IgnoredFilesRegexes.reserve(IgnoredFilesList.size());
41 for (
const StringRef &It : IgnoredFilesList) {
43 IgnoredFilesRegexes.emplace_back(It);
47 void FileChanged(SourceLocation
Loc, FileChangeReason Reason,
48 SrcMgr::CharacteristicKind FileType,
49 FileID PrevFID)
override {
50 if (FileType != clang::SrcMgr::C_User)
53 if (Reason != EnterFile && Reason != ExitFile)
56 FileID
Id = SM.getFileID(
Loc);
60 if (Reason == ExitFile) {
61 if ((Files.size() > 1U) && (Files.back().Id == PrevFID) &&
62 (Files[Files.size() - 2U].Id ==
Id))
67 if (!Files.empty() && Files.back().Id ==
Id)
70 std::optional<llvm::StringRef> FilePath = SM.getNonBuiltinFilenameForID(
Id);
72 FilePath ? llvm::sys::path::filename(*FilePath) :
llvm::StringRef();
75 NextToEnter = Include{
Id,
FileName, SourceLocation()};
77 assert(NextToEnter->Name ==
FileName);
79 Files.emplace_back(*NextToEnter);
83 void InclusionDirective(SourceLocation,
const Token &, StringRef FilePath,
84 bool, CharSourceRange Range,
85 OptionalFileEntryRef File, StringRef, StringRef,
87 SrcMgr::CharacteristicKind FileType)
override {
88 if (FileType != clang::SrcMgr::C_User)
91 llvm::StringRef
FileName = llvm::sys::path::filename(FilePath);
97 FileID
Id = SM.translateFile(*File);
104 void EndOfMainFile()
override {
105 if (!Files.empty() && Files.back().Id == SM.getMainFileID())
108 assert(Files.empty());
111 void checkForDoubleInclude(FileID
Id, llvm::StringRef
FileName,
112 SourceLocation
Loc) {
114 std::find_if(Files.rbegin(), Files.rend(),
115 [&](
const Include &
Entry) { return Entry.Id == Id; });
116 if (It == Files.rend())
119 const std::optional<StringRef> FilePath = SM.getNonBuiltinFilenameForID(
Id);
120 if (!FilePath || isFileIgnored(*FilePath))
123 if (It == Files.rbegin()) {
124 Check.diag(
Loc,
"direct self-inclusion of header file '%0'") <<
FileName;
128 Check.diag(
Loc,
"circular header file dependency detected while including "
129 "'%0', please check the include path")
132 const bool IsIncludePathValid =
133 std::all_of(Files.rbegin(), It + 1, [](
const Include &Elem) {
134 return !Elem.Name.empty() && Elem.Loc.isValid();
136 if (!IsIncludePathValid)
139 for (
const Include &I : llvm::make_range(Files.rbegin(), It + 1))
140 Check.diag(I.Loc,
"'%0' included from here", DiagnosticIDs::Note)
144 bool isFileIgnored(StringRef
FileName)
const {
145 return llvm::any_of(IgnoredFilesRegexes, [&](
const llvm::Regex &It) {
151 std::deque<Include> Files;
152 std::optional<Include> NextToEnter;
153 HeaderIncludeCycleCheck &Check;
154 const SourceManager &SM;
155 std::vector<llvm::Regex> IgnoredFilesRegexes;
163 IgnoredFilesList(utils::options::parseStringList(
164 Options.get(
"IgnoredFilesList",
""))) {}
167 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
169 std::make_unique<CyclicDependencyCallbacks>(*
this, SM, IgnoredFilesList));
llvm::SmallString< 256U > Name
CharSourceRange Range
SourceRange for the file name.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
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.
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap