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.
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.
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"
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;
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;
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 {
55 ConstructorCallback(Replacements* replacements)
56 : replacements_(replacements) {}
58 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
61 Replacements* const replacements_;
64 // Handles replacements for invocations of scoped_ptr<T>(NULL) in an initializer
66 class InitializerCallback : public MatchFinder::MatchCallback {
68 InitializerCallback(Replacements* replacements)
69 : replacements_(replacements) {}
71 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
74 Replacements* const replacements_;
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 {
81 TemporaryCallback(Replacements* replacements) : replacements_(replacements) {}
83 virtual void run(const MatchFinder::MatchResult& result) LLVM_OVERRIDE;
86 Replacements* const replacements_;
89 class EmptyStringConverter {
91 explicit EmptyStringConverter(Replacements* replacements)
92 : constructor_callback_(replacements),
93 initializer_callback_(replacements),
94 temporary_callback_(replacements) {}
96 void SetupMatchers(MatchFinder* match_finder);
99 ConstructorCallback constructor_callback_;
100 InitializerCallback initializer_callback_;
101 TemporaryCallback temporary_callback_;
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(
108 constructExpr(hasDeclaration(methodDecl(ofClass(matchesName(kPattern)))),
110 hasArgument(0, id("arg", expr())),
111 unless(hasArgument(0, defaultArgExpr()))));
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_);
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))
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, ""));
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))
140 const clang::CXXConstructExpr* call =
141 result.Nodes.getNodeAs<clang::CXXConstructExpr>("call");
142 replacements_->insert(Replacement(*result.SourceManager, call, ""));
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))
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),
167 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
169 int main(int argc, const char* argv[]) {
170 CommonOptionsParser options(argc, argv);
171 clang::tooling::ClangTool tool(options.getCompilations(),
172 options.getSourcePathList());
174 Replacements replacements;
175 EmptyStringConverter converter(&replacements);
176 MatchFinder match_finder;
177 converter.SetupMatchers(&match_finder);
180 tool.run(clang::tooling::newFrontendActionFactory(&match_finder));
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();
192 llvm::outs() << "r:" << it->getFilePath() << ":" << it->getOffset() << ":"
193 << it->getLength() << ":" << it->getReplacementText() << "\n";
195 llvm::outs() << "==== END EDITS ====\n";