summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/cpp11-migrate/PassByValue/PassByValueActions.cpp
blob: 612850a70dbf08203d9cc0dfd38f847313e318fc (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
//===-- PassByValueActions.cpp --------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file contains the definition of the ASTMatcher callback for the
/// PassByValue transform.
///
//===----------------------------------------------------------------------===//

#include "PassByValueActions.h"
#include "PassByValueMatchers.h"
#include "Core/IncludeDirectives.h"
#include "Core/Transform.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"

using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;

namespace {
/// \brief \c clang::RecursiveASTVisitor that checks that the given
/// \c ParmVarDecl is used exactly one time.
///
/// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
class ExactlyOneUsageVisitor
    : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
  friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;

public:
  ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl) : ParamDecl(ParamDecl) {}

  /// \brief Whether or not the parameter variable is referred only once in the
  /// given constructor.
  bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
    Count = 0;
    TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
    return Count == 1;
  }

private:
  /// \brief Counts the number of references to a variable.
  ///
  /// Stops the AST traversal if more than one usage is found.
  bool VisitDeclRefExpr(DeclRefExpr *D) {
    if (const ParmVarDecl *To = llvm::dyn_cast<ParmVarDecl>(D->getDecl()))
      if (To == ParamDecl) {
        ++Count;
        if (Count > 1)
          // no need to look further, used more than once
          return false;
      }
    return true;
  }

  const ParmVarDecl *ParamDecl;
  unsigned Count;
};
} // end anonymous namespace

/// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
///
/// Checks both in the init-list and the body of the constructor.
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
                                     const ParmVarDecl *ParamDecl) {
  ExactlyOneUsageVisitor Visitor(ParamDecl);
  return Visitor.hasExactlyOneUsageIn(Ctor);
}

/// \brief Find all references to \p ParamDecl accross all of the
/// redeclarations of \p Ctor.
static void
collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl,
                  llvm::SmallVectorImpl<const ParmVarDecl *> &Results) {
  unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();

  for (CXXConstructorDecl::redecl_iterator I = Ctor->redecls_begin(),
                                           E = Ctor->redecls_end();
       I != E; ++I)
    Results.push_back((*I)->getParamDecl(ParamIdx));
}

void ConstructorParamReplacer::run(const MatchFinder::MatchResult &Result) {
  assert(IncludeManager && "Include directives manager not set.");
  SourceManager &SM = *Result.SourceManager;
  const CXXConstructorDecl *Ctor =
      Result.Nodes.getNodeAs<CXXConstructorDecl>(PassByValueCtorId);
  const ParmVarDecl *ParamDecl =
      Result.Nodes.getNodeAs<ParmVarDecl>(PassByValueParamId);
  const CXXCtorInitializer *Initializer =
      Result.Nodes.getNodeAs<CXXCtorInitializer>(PassByValueInitializerId);
  assert(Ctor && ParamDecl && Initializer && "Bad Callback, missing node.");

  // Check this now to avoid unecessary work. The param locations are checked
  // later.
  if (!Owner.isFileModifiable(SM, Initializer->getSourceLocation()))
    return;

  // The parameter will be in an unspecified state after the move, so check if
  // the parameter is used for anything else other than the copy. If so do not
  // apply any changes.
  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
    return;

  llvm::SmallVector<const ParmVarDecl *, 2> AllParamDecls;
  collectParamDecls(Ctor, ParamDecl, AllParamDecls);

  // Generate all replacements for the params.
  llvm::SmallVector<Replacement, 2> ParamReplaces(AllParamDecls.size());
  for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) {
    TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc();
    SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd());
    CharSourceRange CharRange = Lexer::makeFileCharRange(
        CharSourceRange::getTokenRange(Range), SM, LangOptions());
    TypeLoc ValueTypeLoc = ParamTL;
    // transform non-value parameters (e.g: const-ref) to values
    if (!ParamTL.getNextTypeLoc().isNull())
      ValueTypeLoc = ParamTL.getNextTypeLoc();
    llvm::SmallString<32> ValueStr = Lexer::getSourceText(
        CharSourceRange::getTokenRange(ValueTypeLoc.getSourceRange()), SM,
        LangOptions());

    // If it's impossible to change one of the parameter (e.g: comes from an
    // unmodifiable header) quit the callback now, do not generate any changes.
    if (CharRange.isInvalid() || ValueStr.empty() ||
        !Owner.isFileModifiable(SM, CharRange.getBegin()))
      return;

    // 'const Foo &param' -> 'Foo param'
    //  ~~~~~~~~~~~           ~~~^
    ValueStr += ' ';
    ParamReplaces[I] = Replacement(SM, CharRange, ValueStr);
  }

  // Reject the changes if the the risk level is not acceptable.
  if (!Owner.isAcceptableRiskLevel(RL_Reasonable)) {
    RejectedChanges++;
    return;
  }

  // if needed, include <utility> in the file that uses std::move()
  const FileEntry *STDMoveFile =
      SM.getFileEntryForID(SM.getFileID(Initializer->getLParenLoc()));
  const tooling::Replacement &IncludeReplace =
      IncludeManager->addAngledInclude(STDMoveFile, "utility");
  if (IncludeReplace.isApplicable()) {
    Replaces.insert(IncludeReplace);
    AcceptedChanges++;
  }

  // const-ref params becomes values (const Foo & -> Foo)
  Replaces.insert(ParamReplaces.begin(), ParamReplaces.end());
  AcceptedChanges += ParamReplaces.size();

  // move the value in the init-list
  Replaces.insert(Replacement(
      SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move("));
  Replaces.insert(Replacement(SM, Initializer->getRParenLoc(), 0, ")"));
  AcceptedChanges += 2;
}
OpenPOWER on IntegriCloud