clang-tools 22.0.0git
DuplicateIncludeCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "clang/Frontend/CompilerInstance.h"
12#include "clang/Lex/Preprocessor.h"
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/Support/Regex.h"
16#include <memory>
17
19
20static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM,
21 SourceLocation Start,
22 int Offset) {
23 const FileID Id = SM.getFileID(Start);
24 const unsigned LineNumber = SM.getSpellingLineNumber(Start);
25 while (SM.getFileID(Start) == Id &&
26 SM.getSpellingLineNumber(Start.getLocWithOffset(Offset)) == LineNumber)
27 Start = Start.getLocWithOffset(Offset);
28 return Start;
29}
30
31namespace {
32
33using FileList = SmallVector<StringRef>;
34
35class DuplicateIncludeCallbacks : public PPCallbacks {
36public:
37 DuplicateIncludeCallbacks(DuplicateIncludeCheck &Check,
38 const SourceManager &SM,
39 llvm::ArrayRef<StringRef> IgnoredList);
40
41 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
42 SrcMgr::CharacteristicKind FileType,
43 FileID PrevFID) override;
44
45 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
46 StringRef FileName, bool IsAngled,
47 CharSourceRange FilenameRange,
48 OptionalFileEntryRef File, StringRef SearchPath,
49 StringRef RelativePath, const Module *SuggestedModule,
50 bool ModuleImported,
51 SrcMgr::CharacteristicKind FileType) override;
52
53 void MacroDefined(const Token &MacroNameTok,
54 const MacroDirective *MD) override;
55
56 void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
57 const MacroDirective *Undef) override;
58
59private:
60 // A list of included files is kept for each file we enter.
61 SmallVector<FileList> Files;
62 DuplicateIncludeCheck &Check;
63 const SourceManager &SM;
64 SmallVector<llvm::Regex> AllowedRegexes;
65};
66
67} // namespace
68
70 ClangTidyContext *Context)
71 : ClangTidyCheck(Name, Context),
72 IgnoredFilesList(utils::options::parseStringList(
73 Options.get("IgnoredFilesList", ""))) {}
74
75DuplicateIncludeCallbacks::DuplicateIncludeCallbacks(
76 DuplicateIncludeCheck &Check, const SourceManager &SM,
77 llvm::ArrayRef<StringRef> IgnoredList)
78 : Check(Check), SM(SM) {
79 // The main file doesn't participate in the FileChanged notification.
80 Files.emplace_back();
81
82 AllowedRegexes.reserve(IgnoredList.size());
83 for (const StringRef &It : IgnoredList) {
84 if (!It.empty())
85 AllowedRegexes.emplace_back(It);
86 }
87}
88
89void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
90 FileChangeReason Reason,
91 SrcMgr::CharacteristicKind FileType,
92 FileID PrevFID) {
93 if (Reason == EnterFile)
94 Files.emplace_back();
95 else if (Reason == ExitFile)
96 Files.pop_back();
97}
98
99void DuplicateIncludeCallbacks::InclusionDirective(
100 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
101 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef /*File*/,
102 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
103 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
104 // Skip includes behind macros
105 if (FilenameRange.getBegin().isMacroID() ||
106 FilenameRange.getEnd().isMacroID())
107 return;
108 if (llvm::is_contained(Files.back(), FileName)) {
109 if (llvm::any_of(AllowedRegexes, [&FileName](const llvm::Regex &R) {
110 return R.match(FileName);
111 }))
112 return;
113 // We want to delete the entire line, so make sure that [Start,End] covers
114 // everything.
115 const SourceLocation Start =
116 advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);
117 const SourceLocation End =
118 advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);
119 Check.diag(HashLoc, "duplicate include")
120 << FixItHint::CreateRemoval(SourceRange{Start, End});
121 } else
122 Files.back().push_back(FileName);
123}
124
125void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
126 const MacroDirective *MD) {
127 Files.back().clear();
128}
129
130void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
131 const MacroDefinition &MD,
132 const MacroDirective *Undef) {
133 Files.back().clear();
134}
135
137 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
138 PP->addPPCallbacks(
139 std::make_unique<DuplicateIncludeCallbacks>(*this, SM, IgnoredFilesList));
140}
141
143 Options.store(Opts, "IgnoredFilesList",
144 utils::options::serializeStringList(IgnoredFilesList));
145}
146
147} // namespace clang::tidy::readability
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Find and remove duplicate include directives.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
DuplicateIncludeCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM, SourceLocation Start, int Offset)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap