Home | History | Annotate | Download | only in rewrite_scoped_ptr_ctor_null
      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 }
    199