reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
//===--- DeprecatedIosBaseAliasesCheck.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 "DeprecatedIosBaseAliasesCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace modernize {

static const llvm::SmallVector<StringRef, 5> DeprecatedTypes = {
    {"::std::ios_base::io_state"},
    {"::std::ios_base::open_mode"},
    {"::std::ios_base::seek_dir"},
    {"::std::ios_base::streamoff"},
    {"::std::ios_base::streampos"}};

static const llvm::StringMap<StringRef> ReplacementTypes = {
    {"io_state", "iostate"},
    {"open_mode", "openmode"},
    {"seek_dir", "seekdir"}};

void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) {
  // Only register the matchers for C++; the functionality currently does not
  // provide any benefit to other languages, despite being benign.
  if (!getLangOpts().CPlusPlus)
    return;

  auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl");
  auto IoStateType =
      qualType(hasDeclaration(IoStateDecl), unless(elaboratedType()));

  Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this);
}

void DeprecatedIosBaseAliasesCheck::check(
    const MatchFinder::MatchResult &Result) {
  SourceManager &SM = *Result.SourceManager;

  const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("TypeDecl");
  StringRef TypeName = Typedef->getName();
  bool HasReplacement = ReplacementTypes.count(TypeName);

  const auto *TL = Result.Nodes.getNodeAs<TypeLoc>("TypeLoc");
  SourceLocation IoStateLoc = TL->getBeginLoc();

  // Do not generate fixits for matches depending on template arguments and
  // macro expansions.
  bool Fix = HasReplacement && !TL->getType()->isDependentType();
  if (IoStateLoc.isMacroID()) {
    IoStateLoc = SM.getSpellingLoc(IoStateLoc);
    Fix = false;
  }

  SourceLocation EndLoc = IoStateLoc.getLocWithOffset(TypeName.size() - 1);

  if (HasReplacement) {
    auto FixName = ReplacementTypes.lookup(TypeName);
    auto Builder = diag(IoStateLoc, "'std::ios_base::%0' is deprecated; use "
                                    "'std::ios_base::%1' instead")
                   << TypeName << FixName;

    if (Fix)
      Builder << FixItHint::CreateReplacement(SourceRange(IoStateLoc, EndLoc),
                                              FixName);
  } else
    diag(IoStateLoc, "'std::ios_base::%0' is deprecated") << TypeName;
}

} // namespace modernize
} // namespace tidy
} // namespace clang