aboutsummaryrefslogtreecommitdiff
path: root/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
blob: 3281ef664a7c4308608bd30f98f83f49dfb53ae4 (plain)
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//===--- ReplaceAutoPtrCheck.cpp - clang-tidy------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ReplaceAutoPtrCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"

using namespace clang;
using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace modernize {

namespace {
static const char AutoPtrTokenId[] = "AutoPrTokenId";
static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId";

/// \brief Matches expressions that are lvalues.
///
/// In the following example, a[0] matches expr(isLValue()):
/// \code
///   std::string a[2];
///   std::string b;
///   b = a[0];
///   b = "this string won't match";
/// \endcode
AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }

/// Matches declarations whose declaration context is the C++ standard library
/// namespace std.
///
/// Note that inline namespaces are silently ignored during the lookup since
/// both libstdc++ and libc++ are known to use them for versioning purposes.
///
/// Given:
/// \code
///   namespace ns {
///     struct my_type {};
///     using namespace std;
///   }
///
///   using std::vector;
///   using ns:my_type;
///   using ns::list;
/// \code
///
/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
/// matches "using std::vector" and "using ns::list".
AST_MATCHER(Decl, isFromStdNamespace) {
  const DeclContext *D = Node.getDeclContext();

  while (D->isInlineNamespace())
    D = D->getParent();

  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
    return false;

  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();

  return (Info && Info->isStr("std"));
}

} // namespace

ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
                                         ClangTidyContext *Context)
    : ClangTidyCheck(Name, Context),
      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
          Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}

void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  Options.store(Opts, "IncludeStyle",
                utils::IncludeSorter::toString(IncludeStyle));
}

void ReplaceAutoPtrCheck::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 AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace());
  auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));

  //   std::auto_ptr<int> a;
  //        ^~~~~~~~~~~~~
  //
  //   typedef std::auto_ptr<int> int_ptr_t;
  //                ^~~~~~~~~~~~~
  //
  //   std::auto_ptr<int> fn(std::auto_ptr<int>);
  //        ^~~~~~~~~~~~~         ^~~~~~~~~~~~~
  Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
                                          // Skip elaboratedType() as the named
                                          // type will match soon thereafter.
                                          unless(elaboratedType()))))
                         .bind(AutoPtrTokenId),
                     this);

  //   using std::auto_ptr;
  //   ^~~~~~~~~~~~~~~~~~~
  Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl(
                                   hasName("auto_ptr"), isFromStdNamespace()))))
                         .bind(AutoPtrTokenId),
                     this);

  // Find ownership transfers via copy construction and assignment.
  // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped
  // into std::move().
  //   std::auto_ptr<int> i, j;
  //   i = j;
  //   ~~~~^
  auto MovableArgumentMatcher =
      expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);

  Finder->addMatcher(
      cxxOperatorCallExpr(hasOverloadedOperatorName("="),
                          callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
                          hasArgument(1, MovableArgumentMatcher)),
      this);
  Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
                                      hasArgument(0, MovableArgumentMatcher)),
                     this);
}

void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
  // Only register the preprocessor callbacks for C++; the functionality
  // currently does not provide any benefit to other languages, despite being
  // benign.
  if (!getLangOpts().CPlusPlus)
    return;
  Inserter.reset(new utils::IncludeInserter(
      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}

void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
  SourceManager &SM = *Result.SourceManager;
  if (const auto *E =
          Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
    CharSourceRange Range = Lexer::makeFileCharRange(
        CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());

    if (Range.isInvalid())
      return;

    auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
                << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
                << FixItHint::CreateInsertion(Range.getEnd(), ")");

    if (auto Fix =
            Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
                                             /*IsAngled=*/true))
      Diag << *Fix;

    return;
  }

  SourceLocation AutoPtrLoc;
  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
    //   std::auto_ptr<int> i;
    //        ^
    if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
      AutoPtrLoc = Loc.getTemplateNameLoc();
  } else if (const auto *D =
                 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
    // using std::auto_ptr;
    //            ^
    AutoPtrLoc = D->getNameInfo().getBeginLoc();
  } else {
    llvm_unreachable("Bad Callback. No node provided.");
  }

  if (AutoPtrLoc.isMacroID())
    AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);

  // Ensure that only the 'auto_ptr' token is replaced and not the template
  // aliases.
  if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
      "auto_ptr")
    return;

  SourceLocation EndLoc =
      AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
  diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
      << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
                                      "unique_ptr");
}

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