xref: /llvm-project/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp (revision ed4e505c219fe6c7464ea5a056e90d8cd94c7332)
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 
16 namespace clang::tidy::readability {
17 
advanceBeyondCurrentLine(const SourceManager & SM,SourceLocation Start,int Offset)18 static 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 
29 namespace {
30 
31 using FileList = SmallVector<StringRef>;
32 
33 class DuplicateIncludeCallbacks : public PPCallbacks {
34 public:
DuplicateIncludeCallbacks(DuplicateIncludeCheck & Check,const SourceManager & SM)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 
60 private:
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 
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID)67 void 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 
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,OptionalFileEntryRef File,StringRef SearchPath,StringRef RelativePath,const Module * SuggestedModule,bool ModuleImported,SrcMgr::CharacteristicKind FileType)77 void 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(Files.back(), FileName)) {
87     // We want to delete the entire line, so make sure that [Start,End] covers
88     // everything.
89     SourceLocation Start =
90         advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);
91     SourceLocation End =
92         advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);
93     Check.diag(HashLoc, "duplicate include")
94         << FixItHint::CreateRemoval(SourceRange{Start, End});
95   } else
96     Files.back().push_back(FileName);
97 }
98 
MacroDefined(const Token & MacroNameTok,const MacroDirective * MD)99 void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,
100                                              const MacroDirective *MD) {
101   Files.back().clear();
102 }
103 
MacroUndefined(const Token & MacroNameTok,const MacroDefinition & MD,const MacroDirective * Undef)104 void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,
105                                                const MacroDefinition &MD,
106                                                const MacroDirective *Undef) {
107   Files.back().clear();
108 }
109 
110 } // namespace
111 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)112 void DuplicateIncludeCheck::registerPPCallbacks(
113     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
114   PP->addPPCallbacks(std::make_unique<DuplicateIncludeCallbacks>(*this, SM));
115 }
116 
117 } // namespace clang::tidy::readability
118