1111#include " clang/Lex/Preprocessor.h"
1212#include " llvm/ADT/STLExtras.h"
1313#include " llvm/ADT/SmallVector.h"
14+ #include " llvm/Support/Regex.h"
15+ #include " llvm/Support/Path.h"
16+ #include " ../utils/OptionsUtils.h"
17+ #include " llvm/ADT/StringExtras.h"
1418#include < memory>
19+ #include < vector>
1520
1621namespace clang ::tidy::readability {
1722
@@ -33,12 +38,9 @@ using FileList = SmallVector<StringRef>;
3338class DuplicateIncludeCallbacks : public PPCallbacks {
3439public:
3540 DuplicateIncludeCallbacks (DuplicateIncludeCheck &Check,
36- const SourceManager &SM)
37- : Check(Check), SM(SM) {
38- // The main file doesn't participate in the FileChanged notification.
39- Files.emplace_back ();
40- }
41-
41+ const SourceManager &SM,
42+ const std::vector<std::string> &AllowedStrings);
43+
4244 void FileChanged (SourceLocation Loc, FileChangeReason Reason,
4345 SrcMgr::CharacteristicKind FileType,
4446 FileID PrevFID) override ;
@@ -62,14 +64,28 @@ class DuplicateIncludeCallbacks : public PPCallbacks {
6264 SmallVector<FileList> Files;
6365 DuplicateIncludeCheck &Check;
6466 const SourceManager &SM;
67+ std::vector<llvm::Regex> AllowedDuplicateRegex;
68+
69+ bool IsAllowedDuplicateInclude (StringRef TokenName, OptionalFileEntryRef File,
70+ StringRef RelativePath);
6571};
6672
6773} // namespace
6874
69- void DuplicateIncludeCallbacks::FileChanged (SourceLocation Loc,
70- FileChangeReason Reason,
71- SrcMgr::CharacteristicKind FileType,
72- FileID PrevFID) {
75+ DuplicateIncludeCallbacks::DuplicateIncludeCallbacks (
76+ DuplicateIncludeCheck &Check, const SourceManager &SM,
77+ const std::vector<std::string> &AllowedStrings) : Check(Check), SM(SM) {
78+ // The main file doesn't participate in the FileChanged notification.
79+ Files.emplace_back ();
80+ AllowedDuplicateRegex.reserve (AllowedStrings.size ());
81+ for (const std::string &str : AllowedStrings) {
82+ AllowedDuplicateRegex.emplace_back (str);
83+ }
84+ }
85+
86+ void DuplicateIncludeCallbacks::FileChanged (
87+ SourceLocation Loc, FileChangeReason Reason,
88+ SrcMgr::CharacteristicKind FileType, FileID PrevFID) {
7389 if (Reason == EnterFile)
7490 Files.emplace_back ();
7591 else if (Reason == ExitFile)
@@ -85,6 +101,14 @@ void DuplicateIncludeCallbacks::InclusionDirective(
85101 if (FilenameRange.getBegin ().isMacroID () ||
86102 FilenameRange.getEnd ().isMacroID ())
87103 return ;
104+
105+ // if duplicate allowed, record and return
106+ if (IsAllowedDuplicateInclude (FileName, File, RelativePath))
107+ {
108+ Files.back ().push_back (FileName);
109+ return ;
110+ }
111+
88112 if (llvm::is_contained (Files.back (), FileName)) {
89113 // We want to delete the entire line, so make sure that [Start,End] covers
90114 // everything.
@@ -109,9 +133,54 @@ void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
109133 Files.back ().clear ();
110134}
111135
136+ bool DuplicateIncludeCallbacks::IsAllowedDuplicateInclude (StringRef TokenName,
137+ OptionalFileEntryRef File,
138+ StringRef RelativePath) {
139+ SmallVector<StringRef, 3 > matchArguments;
140+ matchArguments.push_back (TokenName);
141+
142+ if (!RelativePath.empty ())
143+ matchArguments.push_back (llvm::sys::path::filename (RelativePath));
144+
145+ if (File) {
146+ StringRef RealPath = File->getFileEntry ().tryGetRealPathName ();
147+ if (!RealPath.empty ())
148+ matchArguments.push_back (llvm::sys::path::filename (RealPath));
149+ }
150+
151+ // try to match with each regex
152+ for (const llvm::Regex ® : AllowedDuplicateRegex) {
153+ for (StringRef arg : matchArguments) {
154+ if (reg.match (arg))
155+ return true ;
156+ }
157+ }
158+ return false ;
159+ }
160+
161+ DuplicateIncludeCheck::DuplicateIncludeCheck (StringRef Name,
162+ ClangTidyContext *Context)
163+ : ClangTidyCheck(Name, Context) {
164+ std::string Raw = Options.get (" AllowedDuplicateIncludes" , " " ).str ();
165+ if (!Raw.empty ()) {
166+ SmallVector<StringRef, 4 > StringParts;
167+ StringRef (Raw).split (StringParts, ' ,' , -1 , false );
168+
169+ for (StringRef Part : StringParts) {
170+ Part = Part.trim ();
171+ if (!Part.empty ())
172+ AllowedDuplicateIncludes.push_back (Part.str ());
173+ }
174+ }
175+ }
176+
112177void DuplicateIncludeCheck::registerPPCallbacks (
113178 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
114- PP->addPPCallbacks (std::make_unique<DuplicateIncludeCallbacks>(*this , SM));
179+ PP->addPPCallbacks (std::make_unique<DuplicateIncludeCallbacks>(*this , SM, AllowedDuplicateIncludes ));
115180}
116181
182+ void DuplicateIncludeCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
183+ Options.store (Opts, " AllowedDuplicateIncludes" ,
184+ llvm::join (AllowedDuplicateIncludes, " ," ));
185+ }
117186} // namespace clang::tidy::readability
0 commit comments