123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- //===--- DeprecatedHeadersCheck.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 "DeprecatedHeadersCheck.h"
- #include "clang/AST/RecursiveASTVisitor.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Lex/PPCallbacks.h"
- #include "clang/Lex/Preprocessor.h"
- #include "llvm/ADT/StringMap.h"
- #include "llvm/ADT/StringSet.h"
- #include <algorithm>
- #include <vector>
- using IncludeMarker =
- clang::tidy::modernize::DeprecatedHeadersCheck::IncludeMarker;
- namespace clang::tidy::modernize {
- namespace {
- class IncludeModernizePPCallbacks : public PPCallbacks {
- public:
- explicit IncludeModernizePPCallbacks(
- std::vector<IncludeMarker> &IncludesToBeProcessed, LangOptions LangOpts,
- const SourceManager &SM, bool CheckHeaderFile);
- void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
- StringRef FileName, bool IsAngled,
- CharSourceRange FilenameRange,
- OptionalFileEntryRef File, StringRef SearchPath,
- StringRef RelativePath, const Module *Imported,
- SrcMgr::CharacteristicKind FileType) override;
- private:
- std::vector<IncludeMarker> &IncludesToBeProcessed;
- LangOptions LangOpts;
- llvm::StringMap<std::string> CStyledHeaderToCxx;
- llvm::StringSet<> DeleteHeaders;
- const SourceManager &SM;
- bool CheckHeaderFile;
- };
- class ExternCRefutationVisitor
- : public RecursiveASTVisitor<ExternCRefutationVisitor> {
- std::vector<IncludeMarker> &IncludesToBeProcessed;
- const SourceManager &SM;
- public:
- ExternCRefutationVisitor(std::vector<IncludeMarker> &IncludesToBeProcessed,
- SourceManager &SM)
- : IncludesToBeProcessed(IncludesToBeProcessed), SM(SM) {}
- bool shouldWalkTypesOfTypeLocs() const { return false; }
- bool shouldVisitLambdaBody() const { return false; }
- bool VisitLinkageSpecDecl(LinkageSpecDecl *LinkSpecDecl) const {
- if (LinkSpecDecl->getLanguage() != LinkageSpecDecl::lang_c ||
- !LinkSpecDecl->hasBraces())
- return true;
- auto ExternCBlockBegin = LinkSpecDecl->getBeginLoc();
- auto ExternCBlockEnd = LinkSpecDecl->getEndLoc();
- auto IsWrapped = [=, &SM = SM](const IncludeMarker &Marker) -> bool {
- return SM.isBeforeInTranslationUnit(ExternCBlockBegin, Marker.DiagLoc) &&
- SM.isBeforeInTranslationUnit(Marker.DiagLoc, ExternCBlockEnd);
- };
- llvm::erase_if(IncludesToBeProcessed, IsWrapped);
- return true;
- }
- };
- } // namespace
- DeprecatedHeadersCheck::DeprecatedHeadersCheck(StringRef Name,
- ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- CheckHeaderFile(Options.get("CheckHeaderFile", false)) {}
- void DeprecatedHeadersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "CheckHeaderFile", CheckHeaderFile);
- }
- void DeprecatedHeadersCheck::registerPPCallbacks(
- const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
- PP->addPPCallbacks(std::make_unique<IncludeModernizePPCallbacks>(
- IncludesToBeProcessed, getLangOpts(), PP->getSourceManager(),
- CheckHeaderFile));
- }
- void DeprecatedHeadersCheck::registerMatchers(
- ast_matchers::MatchFinder *Finder) {
- // Even though the checker operates on a "preprocessor" level, we still need
- // to act on a "TranslationUnit" to acquire the AST where we can walk each
- // Decl and look for `extern "C"` blocks where we will suppress the report we
- // collected during the preprocessing phase.
- // The `onStartOfTranslationUnit()` won't suffice, since we need some handle
- // to the `ASTContext`.
- Finder->addMatcher(ast_matchers::translationUnitDecl().bind("TU"), this);
- }
- void DeprecatedHeadersCheck::onEndOfTranslationUnit() {
- IncludesToBeProcessed.clear();
- }
- void DeprecatedHeadersCheck::check(
- const ast_matchers::MatchFinder::MatchResult &Result) {
- SourceManager &SM = Result.Context->getSourceManager();
- // Suppress includes wrapped by `extern "C" { ... }` blocks.
- ExternCRefutationVisitor Visitor(IncludesToBeProcessed, SM);
- Visitor.TraverseAST(*Result.Context);
- // Emit all the remaining reports.
- for (const IncludeMarker &Marker : IncludesToBeProcessed) {
- if (Marker.Replacement.empty()) {
- diag(Marker.DiagLoc,
- "including '%0' has no effect in C++; consider removing it")
- << Marker.FileName
- << FixItHint::CreateRemoval(Marker.ReplacementRange);
- } else {
- diag(Marker.DiagLoc, "inclusion of deprecated C++ header "
- "'%0'; consider using '%1' instead")
- << Marker.FileName << Marker.Replacement
- << FixItHint::CreateReplacement(
- Marker.ReplacementRange,
- (llvm::Twine("<") + Marker.Replacement + ">").str());
- }
- }
- }
- IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
- std::vector<IncludeMarker> &IncludesToBeProcessed, LangOptions LangOpts,
- const SourceManager &SM, bool CheckHeaderFile)
- : IncludesToBeProcessed(IncludesToBeProcessed), LangOpts(LangOpts), SM(SM),
- CheckHeaderFile(CheckHeaderFile) {
- for (const auto &KeyValue :
- std::vector<std::pair<llvm::StringRef, std::string>>(
- {{"assert.h", "cassert"},
- {"complex.h", "complex"},
- {"ctype.h", "cctype"},
- {"errno.h", "cerrno"},
- {"float.h", "cfloat"},
- {"limits.h", "climits"},
- {"locale.h", "clocale"},
- {"math.h", "cmath"},
- {"setjmp.h", "csetjmp"},
- {"signal.h", "csignal"},
- {"stdarg.h", "cstdarg"},
- {"stddef.h", "cstddef"},
- {"stdio.h", "cstdio"},
- {"stdlib.h", "cstdlib"},
- {"string.h", "cstring"},
- {"time.h", "ctime"},
- {"wchar.h", "cwchar"},
- {"wctype.h", "cwctype"}})) {
- CStyledHeaderToCxx.insert(KeyValue);
- }
- // Add C++ 11 headers.
- if (LangOpts.CPlusPlus11) {
- for (const auto &KeyValue :
- std::vector<std::pair<llvm::StringRef, std::string>>(
- {{"fenv.h", "cfenv"},
- {"stdint.h", "cstdint"},
- {"inttypes.h", "cinttypes"},
- {"tgmath.h", "ctgmath"},
- {"uchar.h", "cuchar"}})) {
- CStyledHeaderToCxx.insert(KeyValue);
- }
- }
- for (const auto &Key :
- std::vector<std::string>({"stdalign.h", "stdbool.h", "iso646.h"})) {
- DeleteHeaders.insert(Key);
- }
- }
- void IncludeModernizePPCallbacks::InclusionDirective(
- SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
- bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
- StringRef SearchPath, StringRef RelativePath, const Module *Imported,
- SrcMgr::CharacteristicKind FileType) {
- // If we don't want to warn for non-main file reports and this is one, skip
- // it.
- if (!CheckHeaderFile && !SM.isInMainFile(HashLoc))
- return;
- // Ignore system headers.
- if (SM.isInSystemHeader(HashLoc))
- return;
- // FIXME: Take care of library symbols from the global namespace.
- //
- // Reasonable options for the check:
- //
- // 1. Insert std prefix for every such symbol occurrence.
- // 2. Insert `using namespace std;` to the beginning of TU.
- // 3. Do nothing and let the user deal with the migration himself.
- SourceLocation DiagLoc = FilenameRange.getBegin();
- if (CStyledHeaderToCxx.count(FileName) != 0) {
- IncludesToBeProcessed.push_back(
- IncludeMarker{CStyledHeaderToCxx[FileName], FileName,
- FilenameRange.getAsRange(), DiagLoc});
- } else if (DeleteHeaders.count(FileName) != 0) {
- IncludesToBeProcessed.push_back(
- IncludeMarker{std::string{}, FileName,
- SourceRange{HashLoc, FilenameRange.getEnd()}, DiagLoc});
- }
- }
- } // namespace clang::tidy::modernize
|