- add sources.
[platform/framework/web/crosswalk.git] / src / tools / clang / rewrite_scoped_ptr_ctor_null / RewriteScopedPtrCtorNull.cpp
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This implements a Clang tool to convert all instances of std::string("") to
6 // std::string(). The latter is more efficient (as std::string doesn't have to
7 // take a copy of an empty string) and generates fewer instructions as well. It
8 // should be run using the tools/clang/scripts/run_tool.py helper.
9
10 #include "clang/ASTMatchers/ASTMatchers.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Frontend/FrontendActions.h"
14 #include "clang/Tooling/CommonOptionsParser.h"
15 #include "clang/Tooling/Refactoring.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/Support/CommandLine.h"
18
19 using clang::ast_matchers::MatchFinder;
20 using clang::ast_matchers::argumentCountIs;
21 using clang::ast_matchers::bindTemporaryExpr;
22 using clang::ast_matchers::constructorDecl;
23 using clang::ast_matchers::constructExpr;
24 using clang::ast_matchers::defaultArgExpr;
25 using clang::ast_matchers::expr;
26 using clang::ast_matchers::forEach;
27 using clang::ast_matchers::has;
28 using clang::ast_matchers::hasArgument;
29 using clang::ast_matchers::hasDeclaration;
30 using clang::ast_matchers::matchesName;
31 using clang::ast_matchers::id;
32 using clang::ast_matchers::methodDecl;
33 using clang::ast_matchers::newExpr;
34 using clang::ast_matchers::ofClass;
35 using clang::ast_matchers::unless;
36 using clang::ast_matchers::varDecl;
37 using clang::tooling::CommonOptionsParser;
38 using clang::tooling::Replacement;
39 using clang::tooling::Replacements;
40
41 namespace {
42
43 bool IsNullConstant(const clang::Expr& expr, clang::ASTContext* context) {
44   return expr.isNullPointerConstant(*context,
45                                     clang::Expr::NPC_ValueDependentIsNotNull) !=
46          clang::Expr::NPCK_NotNull;
47 }
48
49 // Handles replacements for stack and heap-allocated instances, e.g.:
50 // scoped_ptr<T> a(NULL);
51 // scoped_ptr<T>* b = new scoped_ptr<T>(NULL);
52 // ...though the latter should be pretty rare.
53 class ConstructorCallback : public MatchFinder::MatchCallback {
54  public:
55   ConstructorCallback(Replacements* replacements)
56       : replacements_(replacements) {}
57
58   virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
59
60  private:
61   Replacements* const replacements_;
62 };
63
64 // Handles replacements for invocations of scoped_ptr<T>(NULL) in an initializer
65 // list.
66 class InitializerCallback : public MatchFinder::MatchCallback {
67  public:
68   InitializerCallback(Replacements* replacements)
69       : replacements_(replacements) {}
70
71   virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
72
73  private:
74   Replacements* const replacements_;
75 };
76
77 // Handles replacements for invocations of scoped_ptr<T>(NULL) in a temporary
78 // context, e.g. return scoped_ptr<T>(NULL).
79 class TemporaryCallback : public MatchFinder::MatchCallback {
80  public:
81   TemporaryCallback(Replacements* replacements) : replacements_(replacements) {}
82
83   virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
84
85  private:
86   Replacements* const replacements_;
87 };
88
89 class EmptyStringConverter {
90  public:
91   explicit EmptyStringConverter(Replacements* replacements)
92       : constructor_callback_(replacements),
93         initializer_callback_(replacements),
94         temporary_callback_(replacements) {}
95
96   void SetupMatchers(MatchFinder* match_finder);
97
98  private:
99   ConstructorCallback constructor_callback_;
100   InitializerCallback initializer_callback_;
101   TemporaryCallback temporary_callback_;
102 };
103
104 void EmptyStringConverter::SetupMatchers(MatchFinder* match_finder) {
105   const char kPattern[] = "^::(scoped_ptr|scoped_ptr_malloc)$";
106   const clang::ast_matchers::StatementMatcher& constructor_call = id(
107       "call",
108       constructExpr(hasDeclaration(methodDecl(ofClass(matchesName(kPattern)))),
109                     argumentCountIs(1),
110                     hasArgument(0, id("arg", expr())),
111                     unless(hasArgument(0, defaultArgExpr()))));
112
113   match_finder->addMatcher(varDecl(forEach(constructor_call)),
114                            &constructor_callback_);
115   match_finder->addMatcher(newExpr(has(constructor_call)),
116                            &constructor_callback_);
117   match_finder->addMatcher(bindTemporaryExpr(has(constructor_call)),
118                            &temporary_callback_);
119   match_finder->addMatcher(constructorDecl(forEach(constructor_call)),
120                            &initializer_callback_);
121 }
122
123 void ConstructorCallback::run(const MatchFinder::MatchResult& result) {
124   const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
125   if (!IsNullConstant(*arg, result.Context))
126     return;
127
128   const clang::CXXConstructExpr* call =
129       result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
130   clang::CharSourceRange range =
131       clang::CharSourceRange::getTokenRange(call->getParenRange());
132   replacements_->insert(Replacement(*result.SourceManager, range, ""));
133 }
134
135 void InitializerCallback::run(const MatchFinder::MatchResult& result) {
136   const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
137   if (!IsNullConstant(*arg, result.Context))
138     return;
139
140   const clang::CXXConstructExpr* call =
141       result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
142   replacements_->insert(Replacement(*result.SourceManager, call, ""));
143 }
144
145 void TemporaryCallback::run(const MatchFinder::MatchResult& result) {
146   const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
147   if (!IsNullConstant(*arg, result.Context))
148     return;
149
150   // TODO(dcheng): File a bug with clang. There should be an easier way to do
151   // this replacement, but getTokenRange(call->getParenRange()) and the obvious
152   // (but incorrect) arg both don't work. The former is presumably just buggy,
153   // while the latter probably has to do with the fact that NULL is actually a
154   // macro which expands to a built-in.
155   clang::SourceRange range = arg->getSourceRange();
156   clang::SourceRange expansion_range(
157       result.SourceManager->getExpansionLoc(range.getBegin()),
158       result.SourceManager->getExpansionLoc(range.getEnd()));
159   replacements_->insert(
160       Replacement(*result.SourceManager,
161                   clang::CharSourceRange::getTokenRange(expansion_range),
162                   ""));
163 }
164
165 }  // namespace
166
167 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
168
169 int main(int argc, const char* argv[]) {
170   CommonOptionsParser options(argc, argv);
171   clang::tooling::ClangTool tool(options.getCompilations(),
172                                  options.getSourcePathList());
173
174   Replacements replacements;
175   EmptyStringConverter converter(&replacements);
176   MatchFinder match_finder;
177   converter.SetupMatchers(&match_finder);
178
179   int result =
180       tool.run(clang::tooling::newFrontendActionFactory(&match_finder));
181   if (result != 0)
182     return result;
183
184   // Each replacement line should have the following format:
185   // r:<file path>:<offset>:<length>:<replacement text>
186   // Only the <replacement text> field can contain embedded ":" characters.
187   // TODO(dcheng): Use a more clever serialization.
188   llvm::outs() << "==== BEGIN EDITS ====\n";
189   for (Replacements::const_iterator it = replacements.begin();
190        it != replacements.end();
191        ++it) {
192     llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":"
193                  << it->getLength() << ":" << it->getReplacementText() << "\n";
194   }
195   llvm::outs() << "==== END EDITS ====\n";
196
197   return 0;
198 }