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 rewrite all instances of
6 // scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to
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"
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;
32 namespace ast_matchers {
34 const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl>
37 AST_MATCHER(QualType, isBoolean) {
38 return Node->isBooleanType();
41 } // namespace ast_matchers
46 // Returns true if expr needs to be put in parens (eg: when it is an operator
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)) {
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;
64 class GetRewriterCallback : public MatchFinder::MatchCallback {
66 explicit GetRewriterCallback(Replacements* replacements)
67 : replacements_(replacements) {}
68 virtual void run(const MatchFinder::MatchResult& result) override;
71 Replacements* const replacements_;
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");
79 if (!implicit_call || !arg)
82 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(
83 result.SourceManager->getSpellingLoc(arg->getLocStart()),
84 result.SourceManager->getSpellingLoc(arg->getLocEnd()));
86 return; // TODO(rsleevi): Log an error?
88 // Handle cases where an implicit cast is being done by dereferencing a
89 // pointer to a scoped_refptr<> (sadly, it happens...)
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?
108 if (NeedsParens(sub_expr)) {
109 inner_text.insert(0, "(");
110 inner_text.append(")");
112 inner_text.append("->get()");
113 replacements_->insert(
114 Replacement(*result.SourceManager, range, inner_text));
119 std::string text = clang::Lexer::getSourceText(
120 range, *result.SourceManager, result.Context->getLangOpts());
122 return; // TODO(rsleevi): Log an error?
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();
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.
142 text.append("->get()");
143 replacements_->insert(Replacement(*result.SourceManager, range, text));
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)) {
154 text.append(".get()");
155 replacements_->insert(Replacement(*result.SourceManager, range, text));
158 class VarRewriterCallback : public MatchFinder::MatchCallback {
160 explicit VarRewriterCallback(Replacements* replacements)
161 : replacements_(replacements) {}
162 virtual void run(const MatchFinder::MatchResult& result) override;
165 Replacements* const replacements_;
168 void VarRewriterCallback::run(const MatchFinder::MatchResult& result) {
169 const clang::CXXMemberCallExpr* const implicit_call =
170 result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("call");
171 const clang::DeclaratorDecl* const var_decl =
172 result.Nodes.getNodeAs<clang::DeclaratorDecl>("var");
174 if (!implicit_call || !var_decl)
177 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo();
179 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange(
180 result.SourceManager->getSpellingLoc(tsi->getTypeLoc().getBeginLoc()),
181 result.SourceManager->getSpellingLoc(tsi->getTypeLoc().getEndLoc()));
182 if (!range.isValid())
185 std::string text = clang::Lexer::getSourceText(
186 range, *result.SourceManager, result.Context->getLangOpts());
189 text.erase(text.rfind('*'));
191 std::string replacement_text("scoped_refptr<");
192 replacement_text += text;
193 replacement_text += ">";
195 replacements_->insert(
196 Replacement(*result.SourceManager, range, replacement_text));
201 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage);
203 int main(int argc, const char* argv[]) {
204 llvm::cl::OptionCategory category("Remove scoped_refptr conversions");
205 CommonOptionsParser options(argc, argv, category);
206 clang::tooling::ClangTool tool(options.getCompilations(),
207 options.getSourcePathList());
209 MatchFinder match_finder;
210 Replacements replacements;
212 // Finds all calls to conversion operator member function. This catches calls
213 // to "operator T*", "operator Testable", and "operator bool" equally.
214 auto base_matcher = memberCallExpr(
215 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"),
216 isTemplateInstantiation())),
217 callee(conversionDecl()));
219 // The heuristic for whether or not a conversion is 'unsafe'. An unsafe
220 // conversion is one where a temporary scoped_refptr<T> is converted to
221 // another type. The matcher provides an exception for a temporary
222 // scoped_refptr that is the result of an operator call. In this case, assume
223 // that it's the result of an iterator dereference, and the container itself
224 // retains the necessary reference, since this is a common idiom to see in
226 auto is_unsafe_conversion =
227 bindTemporaryExpr(unless(has(operatorCallExpr())));
229 auto safe_conversion_matcher = memberCallExpr(
230 base_matcher, on(id("arg", expr(unless(is_unsafe_conversion)))));
232 auto unsafe_conversion_matcher =
233 memberCallExpr(base_matcher, on(id("arg", is_unsafe_conversion)));
235 // This catches both user-defined conversions (eg: "operator bool") and
236 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a
237 // pointer to a bool.
238 auto implicit_to_bool =
239 implicitCastExpr(hasImplicitDestinationType(isBoolean()));
241 // Avoid converting calls to of "operator Testable" -> "bool" and calls of
242 // "operator T*" -> "bool".
243 auto bool_conversion_matcher = hasParent(
244 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool)))));
246 // Find all calls to an operator overload that do NOT (ultimately) result in
247 // being cast to a bool - eg: where it's being converted to T* and rewrite
248 // them to add a call to get().
250 // All bool conversions will be handled with the Testable trick, but that
251 // can only be used once "operator T*" is removed, since otherwise it leaves
252 // the call ambiguous.
253 GetRewriterCallback get_callback(&replacements);
254 match_finder.addMatcher(id("call", safe_conversion_matcher), &get_callback);
256 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*.
257 VarRewriterCallback var_callback(&replacements);
258 match_finder.addMatcher(
260 varDecl(hasInitializer(ignoringImpCasts(exprWithCleanups(
261 has(id("call", unsafe_conversion_matcher))))),
262 hasType(pointerType()))),
264 match_finder.addMatcher(
265 constructorDecl(forEachConstructorInitializer(allOf(
266 withInitializer(ignoringImpCasts(
267 exprWithCleanups(has(id("call", unsafe_conversion_matcher))))),
268 forField(id("var", fieldDecl(hasType(pointerType()))))))),
271 std::unique_ptr<clang::tooling::FrontendActionFactory> factory =
272 clang::tooling::newFrontendActionFactory(&match_finder);
273 int result = tool.run(factory.get());
277 // Serialization format is documented in tools/clang/scripts/run_tool.py
278 llvm::outs() << "==== BEGIN EDITS ====\n";
279 for (const auto& r : replacements) {
280 std::string replacement_text = r.getReplacementText().str();
281 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0');
282 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":"
283 << r.getLength() << ":" << replacement_text << "\n";
285 llvm::outs() << "==== END EDITS ====\n";