Skip to content

Commit 669a5ae

Browse files
committed
clang tidy: add header ignore option to include duplicate files
1 parent adc7932 commit 669a5ae

File tree

3 files changed

+88
-15
lines changed

3 files changed

+88
-15
lines changed

clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class CyclicDependencyCallbacks : public PPCallbacks {
107107
Check.diag(Loc, "direct self-inclusion of header file '%0'") << FileName;
108108
return;
109109
}
110-
110+
111111
Check.diag(Loc, "circular header file dependency detected while including "
112112
"'%0', please check the include path")
113113
<< FileName;

clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
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

1621
namespace clang::tidy::readability {
1722

@@ -33,12 +38,9 @@ using FileList = SmallVector<StringRef>;
3338
class DuplicateIncludeCallbacks : public PPCallbacks {
3439
public:
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 &reg : 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+
112177
void 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

clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DUPLICATE_INCLUDE_CHECK_H
1111

1212
#include "../ClangTidyCheck.h"
13-
13+
#include <vector>
14+
#include <string>
1415
namespace clang::tidy::readability {
1516

1617
/// \brief Find and remove duplicate #include directives.
@@ -19,11 +20,14 @@ namespace clang::tidy::readability {
1920
/// directives between them are analyzed.
2021
class DuplicateIncludeCheck : public ClangTidyCheck {
2122
public:
22-
DuplicateIncludeCheck(StringRef Name, ClangTidyContext *Context)
23-
: ClangTidyCheck(Name, Context) {}
23+
DuplicateIncludeCheck(StringRef Name, ClangTidyContext *Context);
2424

2525
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
2626
Preprocessor *ModuleExpanderPP) override;
27+
28+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
29+
private:
30+
std::vector<std::string> AllowedDuplicateIncludes;
2731
};
2832

2933
} // namespace clang::tidy::readability

0 commit comments

Comments
 (0)