1 //===--- DuplicateIncludeCheck.cpp - clang-tidy ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
16 namespace clang::tidy::readability
{
18 static SourceLocation
advanceBeyondCurrentLine(const SourceManager
&SM
,
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
);
31 using FileList
= SmallVector
<StringRef
>;
33 class DuplicateIncludeCallbacks
: public PPCallbacks
{
35 DuplicateIncludeCallbacks(DuplicateIncludeCheck
&Check
,
36 const SourceManager
&SM
)
37 : Check(Check
), SM(SM
) {
38 // The main file doesn't participate in the FileChanged notification.
42 void FileChanged(SourceLocation Loc
, FileChangeReason Reason
,
43 SrcMgr::CharacteristicKind FileType
,
44 FileID PrevFID
) override
;
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
,
52 SrcMgr::CharacteristicKind FileType
) override
;
54 void MacroDefined(const Token
&MacroNameTok
,
55 const MacroDirective
*MD
) override
;
57 void MacroUndefined(const Token
&MacroNameTok
, const MacroDefinition
&MD
,
58 const MacroDirective
*Undef
) override
;
61 // A list of included files is kept for each file we enter.
62 SmallVector
<FileList
> Files
;
63 DuplicateIncludeCheck
&Check
;
64 const SourceManager
&SM
;
67 void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc
,
68 FileChangeReason Reason
,
69 SrcMgr::CharacteristicKind FileType
,
71 if (Reason
== EnterFile
)
73 else if (Reason
== ExitFile
)
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())
86 if (llvm::is_contained(Files
.back(), FileName
)) {
87 // We want to delete the entire line, so make sure that [Start,End] covers
89 SourceLocation Start
=
90 advanceBeyondCurrentLine(SM
, HashLoc
, -1).getLocWithOffset(-1);
92 advanceBeyondCurrentLine(SM
, FilenameRange
.getEnd(), 1);
93 Check
.diag(HashLoc
, "duplicate include")
94 << FixItHint::CreateRemoval(SourceRange
{Start
, End
});
96 Files
.back().push_back(FileName
);
99 void DuplicateIncludeCallbacks::MacroDefined(const Token
&MacroNameTok
,
100 const MacroDirective
*MD
) {
101 Files
.back().clear();
104 void DuplicateIncludeCallbacks::MacroUndefined(const Token
&MacroNameTok
,
105 const MacroDefinition
&MD
,
106 const MacroDirective
*Undef
) {
107 Files
.back().clear();
112 void DuplicateIncludeCheck::registerPPCallbacks(
113 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
114 PP
->addPPCallbacks(std::make_unique
<DuplicateIncludeCallbacks
>(*this, SM
));
117 } // namespace clang::tidy::readability