DeprecatedHeadersCheck.cpp 8.0 KB

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