diff options
author | Richard <legalize@xmission.com> | 2022-01-01 22:47:22 -0700 |
---|---|---|
committer | Richard <legalize@xmission.com> | 2022-01-23 09:23:04 -0700 |
commit | d2e8fb331835fcc565929720781a5fd64e66fc17 (patch) | |
tree | 0ffe4231de17f85dc558497587fa925f17339635 /clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp | |
parent | 2e26633af0c88ea23e3e8783ef60e621f282d3fb (diff) | |
download | llvm-d2e8fb331835fcc565929720781a5fd64e66fc17.zip llvm-d2e8fb331835fcc565929720781a5fd64e66fc17.tar.gz llvm-d2e8fb331835fcc565929720781a5fd64e66fc17.tar.bz2 |
[clang-tidy] Add readability-duplicate-include check
Looks for duplicate includes and removes them.
Every time an include directive is processed, check a vector of filenames
to see if the included file has already been included. If so, it issues
a warning and a replacement to remove the entire line containing the
duplicated include directive.
When a macro is defined or undefined, the vector of filenames is cleared.
This enables including the same file multiple times, but getting
different expansions based on the set of active macros at the time of
inclusion. For example:
#undef NDEBUG
#include "assertion.h"
// ...code with assertions enabled
#define NDEBUG
#include "assertion.h"
// ...code with assertions disabled
Since macros are redefined between the inclusion of assertion.h,
they are not flagged as redundant.
Differential Revision: https://reviews.llvm.org/D7982
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp b/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp new file mode 100644 index 0000000..681b839 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp @@ -0,0 +1,116 @@ +//===--- DuplicateIncludeCheck.cpp - clang-tidy ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DuplicateIncludeCheck.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include <memory> + +namespace clang { +namespace tidy { +namespace readability { + +static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM, + SourceLocation Start, + int Offset) { + const FileID Id = SM.getFileID(Start); + const unsigned LineNumber = SM.getSpellingLineNumber(Start); + while (SM.getFileID(Start) == Id && + SM.getSpellingLineNumber(Start.getLocWithOffset(Offset)) == LineNumber) + Start = Start.getLocWithOffset(Offset); + return Start; +} + +namespace { + +using FileList = SmallVector<StringRef>; + +class DuplicateIncludeCallbacks : public PPCallbacks { +public: + DuplicateIncludeCallbacks(DuplicateIncludeCheck &Check, + const SourceManager &SM) + : Check(Check), SM(SM) { + // The main file doesn't participate in the FileChanged notification. + Files.emplace_back(); + } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override; + + void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, + const MacroDirective *Undef) override; + +private: + // A list of included files is kept for each file we enter. + SmallVector<FileList> Files; + DuplicateIncludeCheck &Check; + const SourceManager &SM; +}; + +void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + if (Reason == EnterFile) + Files.emplace_back(); + else + Files.pop_back(); +} + +void DuplicateIncludeCallbacks::InclusionDirective( + SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) { + if (llvm::find(Files.back(), FileName) != Files.back().end()) { + // We want to delete the entire line, so make sure that [Start,End] covers + // everything. + SourceLocation Start = + advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1); + SourceLocation End = + advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1); + Check.diag(HashLoc, "duplicate include") + << FixItHint::CreateRemoval(SourceRange{Start, End}); + } else + Files.back().push_back(FileName); +} + +void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + Files.back().clear(); +} + +void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok, + const MacroDefinition &MD, + const MacroDirective *Undef) { + Files.back().clear(); +} + +} // namespace + +void DuplicateIncludeCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + PP->addPPCallbacks(std::make_unique<DuplicateIncludeCallbacks>(*this, SM)); +} + +} // namespace readability +} // namespace tidy +} // namespace clang |