1//===--- DuplicateIncludeCheck.cpp - clang-tidy ---------------------------===//
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
9#include "DuplicateIncludeCheck.h"
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
16namespace clang::tidy::readability {
17
18static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM,
19 SourceLocation Start,
20 int Offset) {
21 const FileID Id = SM.getFileID(SpellingLoc: Start);
22 const unsigned LineNumber = SM.getSpellingLineNumber(Loc: Start);
23 while (SM.getFileID(SpellingLoc: Start) == Id &&
24 SM.getSpellingLineNumber(Loc: 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
67void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,
68 FileChangeReason Reason,
69 SrcMgr::CharacteristicKind FileType,
70 FileID PrevFID) {
71 if (Reason == EnterFile)
72 Files.emplace_back();
73 else if (Reason == ExitFile)
74 Files.pop_back();
75}
76
77void DuplicateIncludeCallbacks::InclusionDirective(
78 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
79 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
80 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
81 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
82 // Skip includes behind macros
83 if (FilenameRange.getBegin().isMacroID() ||
84 FilenameRange.getEnd().isMacroID())
85 return;
86 if (llvm::is_contained(Range&: Files.back(), Element: FileName)) {
87 // We want to delete the entire line, so make sure that [Start,End] covers
88 // everything.
89 SourceLocation Start =
90 advanceBeyondCurrentLine(SM, Start: HashLoc, Offset: -1).getLocWithOffset(Offset: -1);
91 SourceLocation End =
92 advanceBeyondCurrentLine(SM, Start: FilenameRange.getEnd(), Offset: 1);
93 Check.diag(Loc: HashLoc, Description: "duplicate include")
94 << FixItHint::CreateRemoval(RemoveRange: SourceRange{Start, End});
95 } else
96 Files.back().push_back(Elt: FileName);
97}
98
99void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
100 const MacroDirective *MD) {
101 Files.back().clear();
102}
103
104void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
105 const MacroDefinition &MD,
106 const MacroDirective *Undef) {
107 Files.back().clear();
108}
109
110} // namespace
111
112void DuplicateIncludeCheck::registerPPCallbacks(
113 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
114 PP->addPPCallbacks(C: std::make_unique<DuplicateIncludeCallbacks>(args&: *this, args: SM));
115}
116
117} // namespace clang::tidy::readability
118

source code of clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp