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

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: Small comment changes, synthesize missing original test for an expected result 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 if (const clang::CXXOperatorCallExpr* op =
mdempsky 2014/08/07 22:25:10 Might be worth adding a comment explaining that th
dcheng 2014/08/07 23:11:21 Done.
55 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
56 return op->getOperator() != clang::OO_Call &&
57 op->getOperator() != clang::OO_Subscript;
58 }
59 return false;
60 }
61
62 class GetRewriterCallback : public MatchFinder::MatchCallback {
63 public:
64 explicit GetRewriterCallback(Replacements* replacements)
65 : replacements_(replacements) {}
66 virtual void run(const MatchFinder::MatchResult& result) override;
67
68 private:
69 Replacements* const replacements_;
70 };
71
72 void GetRewriterCallback::run(const MatchFinder::MatchResult& result) {
73 const clang::CXXMemberCallExpr* const implicit_call =
74 result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("call");
75 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg");
76
77 if (!implicit_call || !arg)
78 return;
79
80 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(
81 result.SourceManager->getSpellingLoc(arg->getLocStart()),
82 result.SourceManager->getSpellingLoc(arg->getLocEnd()));
83 if (!range.isValid())
84 return; // TODO(rsleevi): Log an error?
85
86 // Handle cases where an implicit cast is being done by dereferencing a
87 // pointer to a scoped_refptr<> (sadly, it happens...)
88 //
89 // This rewrites both "*foo" and "*(foo)" as "foo->get()".
90 if (const clang::UnaryOperator* op =
91 llvm::dyn_cast<clang::UnaryOperator>(arg)) {
92 if (op->getOpcode() == clang::UO_Deref) {
93 const clang::Expr* const sub_expr =
94 op->getSubExpr()->IgnoreParenImpCasts();
95 clang::CharSourceRange sub_expr_range =
96 clang::CharSourceRange::getTokenRange(
97 result.SourceManager->getSpellingLoc(sub_expr->getLocStart()),
98 result.SourceManager->getSpellingLoc(sub_expr->getLocEnd()));
99 if (!sub_expr_range.isValid())
100 return; // TODO(rsleevi): Log an error?
101 std::string inner_text = clang::Lexer::getSourceText(
102 sub_expr_range, *result.SourceManager, result.Context->getLangOpts());
103 if (inner_text.empty())
104 return; // TODO(rsleevi): Log an error?
105
106 if (NeedsParens(sub_expr)) {
107 inner_text.insert(0, "(");
108 inner_text.append(")");
109 }
110 inner_text.append("->get()");
111 replacements_->insert(
112 Replacement(*result.SourceManager, range, inner_text));
113 return;
114 }
115 }
116
117 std::string text = clang::Lexer::getSourceText(
118 range, *result.SourceManager, result.Context->getLangOpts());
119 if (text.empty())
120 return; // TODO(rsleevi): Log an error?
121
122 // Unwrap any temporaries - for example, custom iterators that return
123 // scoped_refptr<T> as part of operator*. Any such iterators should also
124 // be declaring a scoped_refptr<T>* operator->, per C++03 24.4.1.1 (Table 72)
125 if (const clang::CXXBindTemporaryExpr* op =
126 llvm::dyn_cast<clang::CXXBindTemporaryExpr>(arg)) {
127 arg = op->getSubExpr();
128 }
129
130 // Handle iterators (which are operator* calls, followed by implicit
131 // conversions) by rewriting *it as it->get()
132 if (const clang::CXXOperatorCallExpr* op =
133 llvm::dyn_cast<clang::CXXOperatorCallExpr>(arg)) {
134 if (op->getOperator() == clang::OO_Star) {
135 text.erase(0, 1);
136 text.append("->get()");
mdempsky 2014/08/07 22:25:10 Is it possible we'll NeedParens() here? E.g., I t
dcheng 2014/08/07 23:11:21 Added a comment per our discussion.
137 replacements_->insert(Replacement(*result.SourceManager, range, text));
138 return;
139 }
140 }
141
142 // The only remaining calls should be non-dereferencing calls (eg: member
143 // calls), so a simple ".get()" appending should suffice.
144 if (NeedsParens(arg)) {
145 text.insert(0, "(");
146 text.append(")");
147 }
148 text.append(".get()");
149 replacements_->insert(Replacement(*result.SourceManager, range, text));
150 }
151
152 } // namespace
153
154 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
155
156 int main(int argc, const char* argv[]) {
157 llvm::cl::OptionCategory category("Remove scoped_refptr conversions");
158 CommonOptionsParser options(argc, argv, category);
159 clang::tooling::ClangTool tool(options.getCompilations(),
160 options.getSourcePathList());
161
162 MatchFinder match_finder;
163
164 // Finds all calls to conversion operator member function. This catches calls
165 // to "operator T*", "operator Testable", and "operator bool" equally.
166 StatementMatcher overloaded_call_matcher = memberCallExpr(
167 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"),
168 isTemplateInstantiation())),
169 callee(conversionDecl()),
170 on(id("arg", expr())));
171
172 // This catches both user-defined conversions (eg: "operator bool") and
173 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a
174 // pointer to a bool.
175 StatementMatcher implicit_to_bool =
176 implicitCastExpr(hasImplicitDestinationType(isBoolean()));
177
178 // Avoid converting calls to of "operator Testable" -> "bool" and calls of
179 // "operator T*" -> "bool".
180 StatementMatcher bool_conversion_matcher = hasParent(expr(
181 anyOf(expr(implicit_to_bool), expr(hasParent(expr(implicit_to_bool))))));
182
183 // Find all calls to an operator overload that do NOT (ultimately) result in
184 // being cast to a bool - eg: where it's being converted to T* and rewrite
185 // them to add a call to get().
186 //
187 // All bool conversions will be handled with the Testable trick, but that
188 // can only be used once "operator T*" is removed, since otherwise it leaves
189 // the call ambiguous.
190 Replacements get_replacements;
191 GetRewriterCallback get_callback(&get_replacements);
192 match_finder.addMatcher(id("call", expr(overloaded_call_matcher)),
193 &get_callback);
194
195 #if 0
196 // Finds all temporary scoped_refptr<T>'s being assigned to a T*. Note that
197 // this will result in two callbacks--both the above callback to append get()
198 // and this callback will match.
199 match_finder.addMatcher(
200 id("var",
201 varDecl(hasInitializer(ignoringImpCasts(
202 id("call", expr(overloaded_call_matcher)))),
203 hasType(pointerType()))),
204 &callback);
205 match_finder.addMatcher(
206 binaryOperator(
207 hasOperatorName("="),
208 hasLHS(declRefExpr(to(id("var", varDecl(hasType(pointerType())))))),
209 hasRHS(ignoringParenImpCasts(
210 id("call", expr(overloaded_call_matcher))))),
211 &callback);
212 #endif
213
214 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
215 clang::tooling::newFrontendActionFactory(&match_finder);
216 int result = tool.run(factory.get());
217 if (result != 0)
218 return result;
219
220 // Serialization format is documented in tools/clang/scripts/run_tool.py
221 llvm::outs() << "==== BEGIN EDITS ====\n";
222 for (const auto& r : get_replacements) {
223 std::string replacement_text = r.getReplacementText().str();
224 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
225 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":"
226 << r.getLength() << ":" << replacement_text << "\n";
227 }
228 llvm::outs() << "==== END EDITS ====\n";
229
230 return 0;
231 }
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