brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.9 KiB · eff7c2f Raw
199 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 "DeprecatedHeadersCheck.h"10#include "clang/AST/RecursiveASTVisitor.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/PPCallbacks.h"13#include "clang/Lex/Preprocessor.h"14#include "llvm/ADT/StringMap.h"15#include "llvm/ADT/StringSet.h"16 17#include <vector>18 19using IncludeMarker =20    clang::tidy::modernize::DeprecatedHeadersCheck::IncludeMarker;21namespace clang::tidy::modernize {22namespace {23 24class IncludeModernizePPCallbacks : public PPCallbacks {25public:26  explicit IncludeModernizePPCallbacks(27      std::vector<IncludeMarker> &IncludesToBeProcessed,28      const LangOptions &LangOpts, const SourceManager &SM,29      bool CheckHeaderFile);30 31  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,32                          StringRef FileName, bool IsAngled,33                          CharSourceRange FilenameRange,34                          OptionalFileEntryRef File, StringRef SearchPath,35                          StringRef RelativePath, const Module *SuggestedModule,36                          bool ModuleImported,37                          SrcMgr::CharacteristicKind FileType) override;38 39private:40  std::vector<IncludeMarker> &IncludesToBeProcessed;41  llvm::StringMap<StringRef> CStyledHeaderToCxx;42  llvm::StringSet<> DeleteHeaders;43  const SourceManager &SM;44  bool CheckHeaderFile;45};46 47class ExternCRefutationVisitor48    : public RecursiveASTVisitor<ExternCRefutationVisitor> {49  std::vector<IncludeMarker> &IncludesToBeProcessed;50  const SourceManager &SM;51 52public:53  ExternCRefutationVisitor(std::vector<IncludeMarker> &IncludesToBeProcessed,54                           SourceManager &SM)55      : IncludesToBeProcessed(IncludesToBeProcessed), SM(SM) {}56  bool shouldWalkTypesOfTypeLocs() const { return false; }57  bool shouldVisitLambdaBody() const { return false; }58 59  bool VisitLinkageSpecDecl(LinkageSpecDecl *LinkSpecDecl) const {60    if (LinkSpecDecl->getLanguage() != LinkageSpecLanguageIDs::C ||61        !LinkSpecDecl->hasBraces())62      return true;63 64    auto ExternCBlockBegin = LinkSpecDecl->getBeginLoc();65    auto ExternCBlockEnd = LinkSpecDecl->getEndLoc();66    auto IsWrapped = [=, &SM = SM](const IncludeMarker &Marker) -> bool {67      return SM.isBeforeInTranslationUnit(ExternCBlockBegin, Marker.DiagLoc) &&68             SM.isBeforeInTranslationUnit(Marker.DiagLoc, ExternCBlockEnd);69    };70 71    llvm::erase_if(IncludesToBeProcessed, IsWrapped);72    return true;73  }74};75} // namespace76 77DeprecatedHeadersCheck::DeprecatedHeadersCheck(StringRef Name,78                                               ClangTidyContext *Context)79    : ClangTidyCheck(Name, Context),80      CheckHeaderFile(Options.get("CheckHeaderFile", false)) {}81 82void DeprecatedHeadersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {83  Options.store(Opts, "CheckHeaderFile", CheckHeaderFile);84}85 86void DeprecatedHeadersCheck::registerPPCallbacks(87    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {88  PP->addPPCallbacks(std::make_unique<IncludeModernizePPCallbacks>(89      IncludesToBeProcessed, getLangOpts(), PP->getSourceManager(),90      CheckHeaderFile));91}92void DeprecatedHeadersCheck::registerMatchers(93    ast_matchers::MatchFinder *Finder) {94  // Even though the checker operates on a "preprocessor" level, we still need95  // to act on a "TranslationUnit" to acquire the AST where we can walk each96  // Decl and look for `extern "C"` blocks where we will suppress the report we97  // collected during the preprocessing phase.98  // The `onStartOfTranslationUnit()` won't suffice, since we need some handle99  // to the `ASTContext`.100  Finder->addMatcher(ast_matchers::translationUnitDecl().bind("TU"), this);101}102 103void DeprecatedHeadersCheck::onEndOfTranslationUnit() {104  IncludesToBeProcessed.clear();105}106 107void DeprecatedHeadersCheck::check(108    const ast_matchers::MatchFinder::MatchResult &Result) {109  SourceManager &SM = Result.Context->getSourceManager();110 111  // Suppress includes wrapped by `extern "C" { ... }` blocks.112  ExternCRefutationVisitor Visitor(IncludesToBeProcessed, SM);113  Visitor.TraverseAST(*Result.Context);114 115  // Emit all the remaining reports.116  for (const IncludeMarker &Marker : IncludesToBeProcessed) {117    if (Marker.Replacement.empty()) {118      diag(Marker.DiagLoc,119           "including '%0' has no effect in C++; consider removing it")120          << Marker.FileName121          << FixItHint::CreateRemoval(Marker.ReplacementRange);122    } else {123      diag(Marker.DiagLoc, "inclusion of deprecated C++ header "124                           "'%0'; consider using '%1' instead")125          << Marker.FileName << Marker.Replacement126          << FixItHint::CreateReplacement(127                 Marker.ReplacementRange,128                 (llvm::Twine("<") + Marker.Replacement + ">").str());129    }130  }131}132 133IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(134    std::vector<IncludeMarker> &IncludesToBeProcessed,135    const LangOptions &LangOpts, const SourceManager &SM, bool CheckHeaderFile)136    : IncludesToBeProcessed(IncludesToBeProcessed), SM(SM),137      CheckHeaderFile(CheckHeaderFile) {138  static constexpr std::pair<StringRef, StringRef> CXX98Headers[] = {139      {"assert.h", "cassert"}, {"complex.h", "complex"},140      {"ctype.h", "cctype"},   {"errno.h", "cerrno"},141      {"float.h", "cfloat"},   {"limits.h", "climits"},142      {"locale.h", "clocale"}, {"math.h", "cmath"},143      {"setjmp.h", "csetjmp"}, {"signal.h", "csignal"},144      {"stdarg.h", "cstdarg"}, {"stddef.h", "cstddef"},145      {"stdio.h", "cstdio"},   {"stdlib.h", "cstdlib"},146      {"string.h", "cstring"}, {"time.h", "ctime"},147      {"wchar.h", "cwchar"},   {"wctype.h", "cwctype"},148  };149  CStyledHeaderToCxx.insert(std::begin(CXX98Headers), std::end(CXX98Headers));150 151  static constexpr std::pair<StringRef, StringRef> CXX11Headers[] = {152      {"fenv.h", "cfenv"},         {"stdint.h", "cstdint"},153      {"inttypes.h", "cinttypes"}, {"tgmath.h", "ctgmath"},154      {"uchar.h", "cuchar"},155  };156  if (LangOpts.CPlusPlus11)157    CStyledHeaderToCxx.insert(std::begin(CXX11Headers), std::end(CXX11Headers));158 159  static constexpr StringRef HeadersToDelete[] = {"stdalign.h", "stdbool.h",160                                                  "iso646.h"};161  DeleteHeaders.insert_range(HeadersToDelete);162}163 164void IncludeModernizePPCallbacks::InclusionDirective(165    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,166    bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,167    StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,168    bool ModuleImported, SrcMgr::CharacteristicKind FileType) {169  // If we don't want to warn for non-main file reports and this is one, skip170  // it.171  if (!CheckHeaderFile && !SM.isInMainFile(HashLoc))172    return;173 174  // Ignore system headers.175  if (SM.isInSystemHeader(HashLoc))176    return;177 178  // FIXME: Take care of library symbols from the global namespace.179  //180  // Reasonable options for the check:181  //182  // 1. Insert std prefix for every such symbol occurrence.183  // 2. Insert `using namespace std;` to the beginning of TU.184  // 3. Do nothing and let the user deal with the migration himself.185  const SourceLocation DiagLoc = FilenameRange.getBegin();186  if (auto It = CStyledHeaderToCxx.find(FileName);187      It != CStyledHeaderToCxx.end()) {188    IncludesToBeProcessed.emplace_back(IncludeMarker{189        It->second, FileName, FilenameRange.getAsRange(), DiagLoc});190  } else if (DeleteHeaders.contains(FileName)) {191    IncludesToBeProcessed.emplace_back(192        // NOLINTNEXTLINE(modernize-use-emplace) - false-positive193        IncludeMarker{StringRef{}, FileName,194                      SourceRange{HashLoc, FilenameRange.getEnd()}, DiagLoc});195  }196}197 198} // namespace clang::tidy::modernize199