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
88void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
89 FileChangeReason Reason,
90 SrcMgr::CharacteristicKind FileType,
91 FileID PrevFID) {
92 if (Reason == EnterFile)
93 Files.emplace_back();
94 else if (Reason == ExitFile)
95 Files.pop_back();
96}
97
98void DuplicateIncludeCallbacks::InclusionDirective(
99 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
100 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef /*File*/,
101 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
102 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
103 // Skip includes behind macros
104 if (FilenameRange.getBegin().isMacroID() ||
105 FilenameRange.getEnd().isMacroID())
106 return;
107 if (llvm::is_contained(Files.back(), FileName)) {
108 if (llvm::any_of(AllowedRegexes, [&FileName](const llvm::Regex &R) {
109 return R.match(FileName);
110 }))
111 return;
112 // We want to delete the entire line, so make sure that [Start,End] covers
113 // everything.
114 const SourceLocation Start =
115 advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);
116 const SourceLocation End =
117 advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);
118 Check.diag(HashLoc, "duplicate include")
119 << FixItHint::CreateRemoval(SourceRange{Start, End});
120 } else
121 Files.back().push_back(FileName);
122}
123
124void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
125 const MacroDirective *MD) {
126 Files.back().clear();
127}
128
129void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
130 const MacroDefinition &MD,
131 const MacroDirective *Undef) {
132 Files.back().clear();
133}
134
136 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
137 PP->addPPCallbacks(
138 std::make_unique<DuplicateIncludeCallbacks>(*this, SM, IgnoredFilesList));
139}
140
142 Options.store(Opts, "IgnoredFilesList",
143 utils::options::serializeStringList(IgnoredFilesList));
144}
145
146} // 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