c9b16e022589abc92723abb83595a1544679cddb
[lldb.git] / clang-tools-extra / remove-cstr-calls / RemoveCStrCalls.cpp
1 //===- examples/Tooling/RemoveCStrCalls.cpp - Redundant c_str call removal ===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file implements a tool that prints replacements that remove redundant
11 //  calls of c_str() on strings.
12 //
13 //  Usage:
14 //  remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
15 //
16 //  Where <cmake-output-dir> is a CMake build directory in which a file named
17 //  compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
18 //  CMake to get this output).
19 //
20 //  <file1> ... specify the paths of files in the CMake source tree. This path
21 //  is looked up in the compile command database. If the path of a file is
22 //  absolute, it needs to point into CMake's source tree. If the path is
23 //  relative, the current working directory needs to be in the CMake source
24 //  tree and the file must be in a subdirectory of the current working
25 //  directory. "./" prefixes in the relative files will be automatically
26 //  removed, but the rest of a relative path must be a suffix of a path in
27 //  the compile command line database.
28 //
29 //  For example, to use remove-cstr-calls on all files in a subtree of the
30 //  source tree, use:
31 //
32 //    /path/in/subtree $ find . -name '*.cpp'|
33 //        xargs remove-cstr-calls /path/to/build
34 //
35 //===----------------------------------------------------------------------===//
36
37 #include "clang/ASTMatchers/ASTMatchers.h"
38 #include "clang/ASTMatchers/ASTMatchFinder.h"
39 #include "clang/Basic/SourceManager.h"
40 #include "clang/Frontend/FrontendActions.h"
41 #include "clang/Lex/Lexer.h"
42 #include "clang/Tooling/CompilationDatabase.h"
43 #include "clang/Tooling/Refactoring.h"
44 #include "clang/Tooling/Tooling.h"
45 #include "llvm/ADT/OwningPtr.h"
46 #include "llvm/ADT/Twine.h"
47 #include "llvm/Support/CommandLine.h"
48 #include "llvm/Support/MemoryBuffer.h"
49 #include "llvm/Support/Path.h"
50 #include "llvm/Support/raw_ostream.h"
51 #include "llvm/Support/system_error.h"
52
53 using namespace clang;
54 using namespace clang::ast_matchers;
55 using namespace llvm;
56 using clang::tooling::newFrontendActionFactory;
57 using clang::tooling::Replacement;
58 using clang::tooling::CompilationDatabase;
59
60 // FIXME: Pull out helper methods in here into more fitting places.
61
62 // Returns the text that makes up 'node' in the source.
63 // Returns an empty string if the text cannot be found.
64 template <typename T>
65 static std::string getText(const SourceManager &SourceManager, const T &Node) {
66   SourceLocation StartSpellingLocatino =
67       SourceManager.getSpellingLoc(Node.getLocStart());
68   SourceLocation EndSpellingLocation =
69       SourceManager.getSpellingLoc(Node.getLocEnd());
70   if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
71     return std::string();
72   }
73   bool Invalid = true;
74   const char *Text =
75       SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
76   if (Invalid) {
77     return std::string();
78   }
79   std::pair<FileID, unsigned> Start =
80       SourceManager.getDecomposedLoc(StartSpellingLocatino);
81   std::pair<FileID, unsigned> End =
82       SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
83           EndSpellingLocation, 0, SourceManager, LangOptions()));
84   if (Start.first != End.first) {
85     // Start and end are in different files.
86     return std::string();
87   }
88   if (End.second < Start.second) {
89     // Shuffling text with macros may cause this.
90     return std::string();
91   }
92   return std::string(Text, End.second - Start.second);
93 }
94
95 // Return true if expr needs to be put in parens when it is an
96 // argument of a prefix unary operator, e.g. when it is a binary or
97 // ternary operator syntactically.
98 static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
99   if (dyn_cast<clang::BinaryOperator>(&ExprNode) ||
100       dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
101     return true;
102   }
103   if (const CXXOperatorCallExpr *op =
104       dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
105     return op->getNumArgs() == 2 &&
106         op->getOperator() != OO_PlusPlus &&
107         op->getOperator() != OO_MinusMinus &&
108         op->getOperator() != OO_Call &&
109         op->getOperator() != OO_Subscript;
110   }
111   return false;
112 }
113
114 // Format a pointer to an expression: prefix with '*' but simplify
115 // when it already begins with '&'.  Return empty string on failure.
116 static std::string formatDereference(const SourceManager &SourceManager,
117                               const Expr &ExprNode) {
118   if (const clang::UnaryOperator *Op =
119       dyn_cast<clang::UnaryOperator>(&ExprNode)) {
120     if (Op->getOpcode() == UO_AddrOf) {
121       // Strip leading '&'.
122       return getText(SourceManager, *Op->getSubExpr()->IgnoreParens());
123     }
124   }
125   const std::string Text = getText(SourceManager, ExprNode);
126   if (Text.empty()) return std::string();
127   // Add leading '*'.
128   if (needParensAfterUnaryOperator(ExprNode)) {
129     return std::string("*(") + Text + ")";
130   }
131   return std::string("*") + Text;
132 }
133
134 namespace {
135 class FixCStrCall : public ast_matchers::MatchFinder::MatchCallback {
136  public:
137   FixCStrCall(tooling::Replacements *Replace)
138       : Replace(Replace) {}
139
140   virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {
141     const CallExpr *Call =
142         Result.Nodes.getStmtAs<CallExpr>("call");
143     const Expr *Arg =
144         Result.Nodes.getStmtAs<Expr>("arg");
145     const bool Arrow =
146         Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow();
147     // Replace the "call" node with the "arg" node, prefixed with '*'
148     // if the call was using '->' rather than '.'.
149     const std::string ArgText = Arrow ?
150         formatDereference(*Result.SourceManager, *Arg) :
151         getText(*Result.SourceManager, *Arg);
152     if (ArgText.empty()) return;
153
154     Replace->insert(Replacement(*Result.SourceManager, Call, ArgText));
155   }
156
157  private:
158   tooling::Replacements *Replace;
159 };
160 } // end namespace
161
162 const char *StringConstructor =
163     "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
164     "::basic_string";
165
166 const char *StringCStrMethod =
167     "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
168     "::c_str";
169
170 cl::opt<std::string> BuildPath(
171   cl::Positional,
172   cl::desc("<build-path>"));
173
174 cl::list<std::string> SourcePaths(
175   cl::Positional,
176   cl::desc("<source0> [... <sourceN>]"),
177   cl::OneOrMore);
178
179 int main(int argc, const char **argv) {
180   llvm::OwningPtr<CompilationDatabase> Compilations(
181     tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv));
182   cl::ParseCommandLineOptions(argc, argv);
183   if (!Compilations) {
184     std::string ErrorMessage;
185     Compilations.reset(
186            CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage));
187     if (!Compilations)
188       llvm::report_fatal_error(ErrorMessage);
189     }
190   tooling::RefactoringTool Tool(*Compilations, SourcePaths);
191   ast_matchers::MatchFinder Finder;
192   FixCStrCall Callback(&Tool.getReplacements());
193   Finder.addMatcher(
194       constructorCall(
195           hasDeclaration(method(hasName(StringConstructor))),
196           argumentCountIs(2),
197           // The first argument must have the form x.c_str() or p->c_str()
198           // where the method is string::c_str().  We can use the copy
199           // constructor of string instead (or the compiler might share
200           // the string object).
201           hasArgument(
202               0,
203               id("call", memberCall(
204                   callee(id("member", memberExpression())),
205                   callee(method(hasName(StringCStrMethod))),
206                   on(id("arg", expression()))))),
207           // The second argument is the alloc object which must not be
208           // present explicitly.
209           hasArgument(
210               1,
211               defaultArgument())),
212       &Callback);
213   Finder.addMatcher(
214       constructorCall(
215           // Implicit constructors of these classes are overloaded
216           // wrt. string types and they internally make a StringRef
217           // referring to the argument.  Passing a string directly to
218           // them is preferred to passing a char pointer.
219           hasDeclaration(method(anyOf(
220               hasName("::llvm::StringRef::StringRef"),
221               hasName("::llvm::Twine::Twine")))),
222           argumentCountIs(1),
223           // The only argument must have the form x.c_str() or p->c_str()
224           // where the method is string::c_str().  StringRef also has
225           // a constructor from string which is more efficient (avoids
226           // strlen), so we can construct StringRef from the string
227           // directly.
228           hasArgument(
229               0,
230               id("call", memberCall(
231                   callee(id("member", memberExpression())),
232                   callee(method(hasName(StringCStrMethod))),
233                   on(id("arg", expression())))))),
234       &Callback);
235   return Tool.run(newFrontendActionFactory(&Finder));
236 }
237