brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · 4842fe2 Raw
148 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "DuplicateIncludeCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/Preprocessor.h"13#include "llvm/ADT/STLExtras.h"14#include "llvm/ADT/SmallVector.h"15#include "llvm/Support/Regex.h"16#include <memory>17 18namespace clang::tidy::readability {19 20static SourceLocation advanceBeyondCurrentLine(const SourceManager &SM,21                                               SourceLocation Start,22                                               int Offset) {23  const FileID Id = SM.getFileID(Start);24  const unsigned LineNumber = SM.getSpellingLineNumber(Start);25  while (SM.getFileID(Start) == Id &&26         SM.getSpellingLineNumber(Start.getLocWithOffset(Offset)) == LineNumber)27    Start = Start.getLocWithOffset(Offset);28  return Start;29}30 31namespace {32 33using FileList = SmallVector<StringRef>;34 35class DuplicateIncludeCallbacks : public PPCallbacks {36public:37  DuplicateIncludeCallbacks(DuplicateIncludeCheck &Check,38                            const SourceManager &SM,39                            llvm::ArrayRef<StringRef> IgnoredList);40 41  void FileChanged(SourceLocation Loc, FileChangeReason Reason,42                   SrcMgr::CharacteristicKind FileType,43                   FileID PrevFID) override;44 45  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,46                          StringRef FileName, bool IsAngled,47                          CharSourceRange FilenameRange,48                          OptionalFileEntryRef File, StringRef SearchPath,49                          StringRef RelativePath, const Module *SuggestedModule,50                          bool ModuleImported,51                          SrcMgr::CharacteristicKind FileType) override;52 53  void MacroDefined(const Token &MacroNameTok,54                    const MacroDirective *MD) override;55 56  void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,57                      const MacroDirective *Undef) override;58 59private:60  // A list of included files is kept for each file we enter.61  SmallVector<FileList> Files;62  DuplicateIncludeCheck &Check;63  const SourceManager &SM;64  SmallVector<llvm::Regex> AllowedRegexes;65};66 67} // namespace68 69DuplicateIncludeCheck::DuplicateIncludeCheck(StringRef Name,70                                             ClangTidyContext *Context)71    : ClangTidyCheck(Name, Context),72      IgnoredFilesList(utils::options::parseStringList(73          Options.get("IgnoredFilesList", ""))) {}74 75DuplicateIncludeCallbacks::DuplicateIncludeCallbacks(76    DuplicateIncludeCheck &Check, const SourceManager &SM,77    llvm::ArrayRef<StringRef> IgnoredList)78    : Check(Check), SM(SM) {79  // The main file doesn't participate in the FileChanged notification.80  Files.emplace_back();81 82  AllowedRegexes.reserve(IgnoredList.size());83  for (const StringRef &It : IgnoredList) {84    if (!It.empty())85      AllowedRegexes.emplace_back(It);86  }87}88 89void DuplicateIncludeCallbacks::FileChanged(SourceLocation Loc,90                                            FileChangeReason Reason,91                                            SrcMgr::CharacteristicKind FileType,92                                            FileID PrevFID) {93  if (Reason == EnterFile)94    Files.emplace_back();95  else if (Reason == ExitFile)96    Files.pop_back();97}98 99void DuplicateIncludeCallbacks::InclusionDirective(100    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,101    bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef /*File*/,102    StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,103    bool ModuleImported, SrcMgr::CharacteristicKind FileType) {104  // Skip includes behind macros105  if (FilenameRange.getBegin().isMacroID() ||106      FilenameRange.getEnd().isMacroID())107    return;108  if (llvm::is_contained(Files.back(), FileName)) {109    if (llvm::any_of(AllowedRegexes, [&FileName](const llvm::Regex &R) {110          return R.match(FileName);111        }))112      return;113    // We want to delete the entire line, so make sure that [Start,End] covers114    // everything.115    const SourceLocation Start =116        advanceBeyondCurrentLine(SM, HashLoc, -1).getLocWithOffset(-1);117    const SourceLocation End =118        advanceBeyondCurrentLine(SM, FilenameRange.getEnd(), 1);119    Check.diag(HashLoc, "duplicate include")120        << FixItHint::CreateRemoval(SourceRange{Start, End});121  } else122    Files.back().push_back(FileName);123}124 125void DuplicateIncludeCallbacks::MacroDefined(const Token &MacroNameTok,126                                             const MacroDirective *MD) {127  Files.back().clear();128}129 130void DuplicateIncludeCallbacks::MacroUndefined(const Token &MacroNameTok,131                                               const MacroDefinition &MD,132                                               const MacroDirective *Undef) {133  Files.back().clear();134}135 136void DuplicateIncludeCheck::registerPPCallbacks(137    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {138  PP->addPPCallbacks(139      std::make_unique<DuplicateIncludeCallbacks>(*this, SM, IgnoredFilesList));140}141 142void DuplicateIncludeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {143  Options.store(Opts, "IgnoredFilesList",144                utils::options::serializeStringList(IgnoredFilesList));145}146 147} // namespace clang::tidy::readability148