Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp

Issue 446203003: scoped_refptr implicit conversion cleanup tool. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 rewrite all instances of
6 // scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to
7 // the .get() method.
8
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/ASTMatchers/ASTMatchersMacros.h"
16 #include "clang/ASTMatchers/ASTMatchFinder.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Frontend/FrontendActions.h"
19 #include "clang/Lex/Lexer.h"
20 #include "clang/Tooling/CommonOptionsParser.h"
21 #include "clang/Tooling/Refactoring.h"
22 #include "clang/Tooling/Tooling.h"
23 #include "llvm/Support/CommandLine.h"
24
25 using namespace clang::ast_matchers;
26 using clang::tooling::CommonOptionsParser;
27 using clang::tooling::Replacement;
28 using clang::tooling::Replacements;
29 using llvm::StringRef;
30
31 namespace clang {
32 namespace ast_matchers {
33
34 const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl>
35 conversionDecl;
36
37 AST_MATCHER(QualType, isBoolean) {
38 return Node->isBooleanType();
39 }
40
41 } // namespace ast_matchers
42 } // namespace clang
43
44 namespace {
45
46 // Returns true if expr needs to be put in parens (eg: when it is an operator
47 // syntactically).
48 bool NeedsParens(const clang::Expr* expr) {
49 if (llvm::dyn_cast<clang::UnaryOperator>(expr) ||
50 llvm::dyn_cast<clang::BinaryOperator>(expr) ||
51 llvm::dyn_cast<clang::ConditionalOperator>(expr)) {
52 return true;
53 }
54 // Calls to an overloaded operator also need parens, except for foo(...) and
55 // foo[...] expressions.
56 if (const clang::CXXOperatorCallExpr* op =
57 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
58 return op->getOperator() != clang::OO_Call &&
59 op->getOperator() != clang::OO_Subscript;
60 }
61 return false;
62 }
63
64 class GetRewriterCallback : public MatchFinder::MatchCallback {
65 public:
66 explicit GetRewriterCallback(Replacements* replacements)
67 : replacements_(replacements) {}
68 virtual void run(const MatchFinder::MatchResult& result) override;
69
70 private:
71 Replacements* const replacements_;
72 };
73
74 void GetRewriterCallback::run(const MatchFinder::MatchResult& result) {
75 const clang::CXXMemberCallExpr* const implicit_call =
76 result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("call");
77 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
78
79 if (!implicit_call || !arg)
80 return;
81
82 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(
83 result.SourceManager->getSpellingLoc(arg->getLocStart()),
84 result.SourceManager->getSpellingLoc(arg->getLocEnd()));
85 if (!range.isValid())
86 return; // TODO(rsleevi): Log an error?
87
88 // Handle cases where an implicit cast is being done by dereferencing a
89 // pointer to a scoped_refptr<> (sadly, it happens...)
90 //
91 // This rewrites both "*foo" and "*(foo)" as "foo->get()".
92 if (const clang::UnaryOperator* op =
93 llvm::dyn_cast<clang::UnaryOperator>(arg)) {
94 if (op->getOpcode() == clang::UO_Deref) {
95 const clang::Expr* const sub_expr =
96 op->getSubExpr()->IgnoreParenImpCasts();
97 clang::CharSourceRange sub_expr_range =
98 clang::CharSourceRange::getTokenRange(
99 result.SourceManager->getSpellingLoc(sub_expr->getLocStart()),
100 result.SourceManager->getSpellingLoc(sub_expr->getLocEnd()));
101 if (!sub_expr_range.isValid())
102 return; // TODO(rsleevi): Log an error?
103 std::string inner_text = clang::Lexer::getSourceText(
104 sub_expr_range, *result.SourceManager, result.Context->getLangOpts());
105 if (inner_text.empty())
106 return; // TODO(rsleevi): Log an error?
107
108 if (NeedsParens(sub_expr)) {
109 inner_text.insert(0, "(");
110 inner_text.append(")");
111 }
112 inner_text.append("->get()");
113 replacements_->insert(
114 Replacement(*result.SourceManager, range, inner_text));
115 return;
116 }
117 }
118
119 std::string text = clang::Lexer::getSourceText(
120 range, *result.SourceManager, result.Context->getLangOpts());
121 if (text.empty())
122 return; // TODO(rsleevi): Log an error?
123
124 // Unwrap any temporaries - for example, custom iterators that return
125 // scoped_refptr<T> as part of operator*. Any such iterators should also
126 // be declaring a scoped_refptr<T>* operator->, per C++03 24.4.1.1 (Table 72)
127 if (const clang::CXXBindTemporaryExpr* op =
128 llvm::dyn_cast<clang::CXXBindTemporaryExpr>(arg)) {
129 arg = op->getSubExpr();
130 }
131
132 // Handle iterators (which are operator* calls, followed by implicit
133 // conversions) by rewriting *it as it->get()
134 if (const clang::CXXOperatorCallExpr* op =
135 llvm::dyn_cast<clang::CXXOperatorCallExpr>(arg)) {
136 if (op->getOperator() == clang::OO_Star) {
137 // Note that this doesn't rewrite **it correctly, since it should be
138 // rewritten using parens, e.g. (*it)->get(). However, this shouldn't
139 // happen frequently, if at all, since it would likely indicate code is
140 // storing pointers to a scoped_refptr in a container.
141 text.erase(0, 1);
142 text.append("->get()");
143 replacements_->insert(Replacement(*result.SourceManager, range, text));
144 return;
145 }
146 }
147
148 // The only remaining calls should be non-dereferencing calls (eg: member
149 // calls), so a simple ".get()" appending should suffice.
150 if (NeedsParens(arg)) {
151 text.insert(0, "(");
152 text.append(")");
153 }
154 text.append(".get()");
155 replacements_->insert(Replacement(*result.SourceManager, range, text));
156 }
157
158 } // namespace
159
160 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
161
162 int main(int argc, const char* argv[]) {
163 llvm::cl::OptionCategory category("Remove scoped_refptr conversions");
164 CommonOptionsParser options(argc, argv, category);
165 clang::tooling::ClangTool tool(options.getCompilations(),
166 options.getSourcePathList());
167
168 MatchFinder match_finder;
169
170 // Finds all calls to conversion operator member function. This catches calls
171 // to "operator T*", "operator Testable", and "operator bool" equally.
172 StatementMatcher overloaded_call_matcher = memberCallExpr(
173 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"),
174 isTemplateInstantiation())),
175 callee(conversionDecl()),
176 on(id("arg", expr())));
177
178 // This catches both user-defined conversions (eg: "operator bool") and
179 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a
180 // pointer to a bool.
181 StatementMatcher implicit_to_bool =
182 implicitCastExpr(hasImplicitDestinationType(isBoolean()));
183
184 // Avoid converting calls to of "operator Testable" -> "bool" and calls of
185 // "operator T*" -> "bool".
186 StatementMatcher bool_conversion_matcher = hasParent(expr(
187 anyOf(expr(implicit_to_bool), expr(hasParent(expr(implicit_to_bool))))));
188
189 // Find all calls to an operator overload that do NOT (ultimately) result in
190 // being cast to a bool - eg: where it's being converted to T* and rewrite
191 // them to add a call to get().
192 //
193 // All bool conversions will be handled with the Testable trick, but that
194 // can only be used once "operator T*" is removed, since otherwise it leaves
195 // the call ambiguous.
196 Replacements get_replacements;
197 GetRewriterCallback get_callback(&get_replacements);
198 match_finder.addMatcher(id("call", expr(overloaded_call_matcher)),
199 &get_callback);
200
201 #if 0
202 // Finds all temporary scoped_refptr<T>'s being assigned to a T*. Note that
203 // this will result in two callbacks--both the above callback to append get()
204 // and this callback will match.
205 match_finder.addMatcher(
206 id("var",
207 varDecl(hasInitializer(ignoringImpCasts(
208 id("call", expr(overloaded_call_matcher)))),
209 hasType(pointerType()))),
210 &callback);
211 match_finder.addMatcher(
212 binaryOperator(
213 hasOperatorName("="),
214 hasLHS(declRefExpr(to(id("var", varDecl(hasType(pointerType())))))),
215 hasRHS(ignoringParenImpCasts(
216 id("call", expr(overloaded_call_matcher))))),
217 &callback);
218 #endif
219
220 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
221 clang::tooling::newFrontendActionFactory(&match_finder);
222 int result = tool.run(factory.get());
223 if (result != 0)
224 return result;
225
226 // Serialization format is documented in tools/clang/scripts/run_tool.py
227 llvm::outs() << "==== BEGIN EDITS ====\n";
228 for (const auto& r : get_replacements) {
229 std::string replacement_text = r.getReplacementText().str();
230 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
231 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":"
232 << r.getLength() << ":" << replacement_text << "\n";
233 }
234 llvm::outs() << "==== END EDITS ====\n";
235
236 return 0;
237 }
OLDNEW
« no previous file with comments | « tools/clang/rewrite_scoped_refptr/Makefile ('k') | tools/clang/rewrite_scoped_refptr/tests/scoped_refptr.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698