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
10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/Preprocessor.h"
12#include "llvm/ADT/STLExtras.h"
13#include "llvm/ADT/SmallVector.h"
14#include <memory>
15
17
18static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM,
19 SourceLocation Start,
20 int Offset) {
21 const FileID Id = SM.getFileID(Start);
22 const unsigned LineNumber = SM.getSpellingLineNumber(Start);
23 while (SM.getFileID(Start) == Id &&
24 SM.getSpellingLineNumber(Start.getLocWithOffset(Offset)) == LineNumber)
25 Start = Start.getLocWithOffset(Offset);
26 return Start;
27}
28
29namespace {
30
31using FileList = SmallVector<StringRef>;
32
33class DuplicateIncludeCallbacks : public PPCallbacks {
34public:
35 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
42 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
43 SrcMgr::CharacteristicKind FileType,
44 FileID PrevFID) override;
45
46 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
47 StringRef FileName, bool IsAngled,
48 CharSourceRange FilenameRange,
49 OptionalFileEntryRef File, StringRef SearchPath,
50 StringRef RelativePath, const Module *SuggestedModule,
51 bool ModuleImported,
52 SrcMgr::CharacteristicKind FileType) override;
53
54 void MacroDefined(const Token &MacroNameTok,
55 const MacroDirective *MD) override;
56
57 void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
58 const MacroDirective *Undef) override;
59
60private:
61 // A list of included files is kept for each file we enter.
62 SmallVector<FileList> Files;
63 DuplicateIncludeCheck &Check;
64 const SourceManager &SM;
65};
66
67} // namespace
68
69void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
70 FileChangeReason Reason,
71 SrcMgr::CharacteristicKind FileType,
72 FileID PrevFID) {
73 if (Reason == EnterFile)
74 Files.emplace_back();
75 else if (Reason == ExitFile)
76 Files.pop_back();
77}
78
79void DuplicateIncludeCallbacks::InclusionDirective(
80 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
81 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
82 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
83 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
84 // Skip includes behind macros
85 if (FilenameRange.getBegin().isMacroID() ||
86 FilenameRange.getEnd().isMacroID())
87 return;
88 if (llvm::is_contained(Files.back(), FileName)) {
89 // We want to delete the entire line, so make sure that [Start,End] covers
90 // everything.
91 SourceLocation Start =
92 advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);
93 SourceLocation End =
94 advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);
95 Check.diag(HashLoc, "duplicate include")
96 << FixItHint::CreateRemoval(SourceRange{Start, End});
97 } else
98 Files.back().push_back(FileName);
99}
100
101void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
102 const MacroDirective *MD) {
103 Files.back().clear();
104}
105
106void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
107 const MacroDefinition &MD,
108 const MacroDirective *Undef) {
109 Files.back().clear();
110}
111
113 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
114 PP->addPPCallbacks(std::make_unique<DuplicateIncludeCallbacks>(*this, SM));
115}
116
117} // namespace clang::tidy::readability
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM, SourceLocation Start, int Offset)