OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // This implements a Clang tool to rewrite all instances of | 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 | 6 // scoped_refptr<T>'s implicit cast to T (operator T*) to an explicit call to |
7 // the .get() method. | 7 // the .get() method. |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <memory> | 10 #include <memory> |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
54 // Calls to an overloaded operator also need parens, except for foo(...) and | 54 // Calls to an overloaded operator also need parens, except for foo(...) and |
55 // foo[...] expressions. | 55 // foo[...] expressions. |
56 if (const clang::CXXOperatorCallExpr* op = | 56 if (const clang::CXXOperatorCallExpr* op = |
57 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { | 57 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { |
58 return op->getOperator() != clang::OO_Call && | 58 return op->getOperator() != clang::OO_Call && |
59 op->getOperator() != clang::OO_Subscript; | 59 op->getOperator() != clang::OO_Subscript; |
60 } | 60 } |
61 return false; | 61 return false; |
62 } | 62 } |
63 | 63 |
64 Replacement RewriteRawPtrToScopedRefptr(const MatchFinder::MatchResult& result, | |
65 clang::SourceLocation begin, | |
66 clang::SourceLocation end) { | |
67 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
68 result.SourceManager->getSpellingLoc(begin), | |
69 result.SourceManager->getSpellingLoc(end)); | |
70 | |
71 std::string text = clang::Lexer::getSourceText( | |
72 range, *result.SourceManager, result.Context->getLangOpts()); | |
73 text.erase(text.rfind('*')); | |
74 | |
75 std::string replacement_text("scoped_refptr<"); | |
76 replacement_text += text; | |
77 replacement_text += ">"; | |
78 | |
79 return Replacement(*result.SourceManager, range, replacement_text); | |
80 } | |
81 | |
64 class GetRewriterCallback : public MatchFinder::MatchCallback { | 82 class GetRewriterCallback : public MatchFinder::MatchCallback { |
65 public: | 83 public: |
66 explicit GetRewriterCallback(Replacements* replacements) | 84 explicit GetRewriterCallback(Replacements* replacements) |
67 : replacements_(replacements) {} | 85 : replacements_(replacements) {} |
68 virtual void run(const MatchFinder::MatchResult& result) override; | 86 virtual void run(const MatchFinder::MatchResult& result) override; |
69 | 87 |
70 private: | 88 private: |
71 Replacements* const replacements_; | 89 Replacements* const replacements_; |
72 }; | 90 }; |
73 | 91 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
159 public: | 177 public: |
160 explicit VarRewriterCallback(Replacements* replacements) | 178 explicit VarRewriterCallback(Replacements* replacements) |
161 : replacements_(replacements) {} | 179 : replacements_(replacements) {} |
162 virtual void run(const MatchFinder::MatchResult& result) override; | 180 virtual void run(const MatchFinder::MatchResult& result) override; |
163 | 181 |
164 private: | 182 private: |
165 Replacements* const replacements_; | 183 Replacements* const replacements_; |
166 }; | 184 }; |
167 | 185 |
168 void VarRewriterCallback::run(const MatchFinder::MatchResult& result) { | 186 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 = | 187 const clang::DeclaratorDecl* const var_decl = |
172 result.Nodes.getNodeAs<clang::DeclaratorDecl>("var"); | 188 result.Nodes.getNodeAs<clang::DeclaratorDecl>("var"); |
173 | 189 |
174 if (!implicit_call || !var_decl) | 190 if (!var_decl) |
175 return; | 191 return; |
176 | 192 |
177 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo(); | 193 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo(); |
178 | 194 |
179 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | 195 // TODO(dcheng): This mishandles a case where a variable has multiple |
180 result.SourceManager->getSpellingLoc(tsi->getTypeLoc().getBeginLoc()), | 196 // declarations. Oh well. |
Ryan Sleevi
2014/08/14 21:26:39
Example?
dcheng
2014/08/14 21:40:12
Done.
| |
181 result.SourceManager->getSpellingLoc(tsi->getTypeLoc().getEndLoc())); | 197 replacements_->insert(RewriteRawPtrToScopedRefptr( |
182 if (!range.isValid()) | 198 result, tsi->getTypeLoc().getBeginLoc(), tsi->getTypeLoc().getEndLoc())); |
199 } | |
200 | |
201 class FunctionRewriterCallback : public MatchFinder::MatchCallback { | |
202 public: | |
203 explicit FunctionRewriterCallback(Replacements* replacements) | |
204 : replacements_(replacements) {} | |
205 virtual void run(const MatchFinder::MatchResult& result) override; | |
206 | |
207 private: | |
208 Replacements* const replacements_; | |
209 }; | |
210 | |
211 void FunctionRewriterCallback::run(const MatchFinder::MatchResult& result) { | |
212 const clang::FunctionDecl* const function_decl = | |
213 result.Nodes.getNodeAs<clang::FunctionDecl>("fn"); | |
214 | |
215 if (!function_decl) | |
183 return; | 216 return; |
184 | 217 |
185 std::string text = clang::Lexer::getSourceText( | 218 // If matched against an implicit conversion to a DeclRefExpr, make sure the |
186 range, *result.SourceManager, result.Context->getLangOpts()); | 219 // referenced declaration is of class type, e.g. the tool skips trying to |
187 if (text.empty()) | 220 // chase pointers/references to determine if the pointee is a scoped_refptr<T> |
221 // with local storage. Instead, let a human manually handle those cases. | |
222 const clang::VarDecl* const var_decl = | |
223 result.Nodes.getNodeAs<clang::VarDecl>("var"); | |
224 if (var_decl && !var_decl->getTypeSourceInfo()->getType()->isClassType()) { | |
188 return; | 225 return; |
189 text.erase(text.rfind('*')); | 226 } |
190 | 227 |
191 std::string replacement_text("scoped_refptr<"); | 228 for (clang::FunctionDecl* f : function_decl->redecls()) { |
192 replacement_text += text; | 229 clang::SourceRange range = f->getReturnTypeSourceRange(); |
193 replacement_text += ">"; | 230 replacements_->insert( |
194 | 231 RewriteRawPtrToScopedRefptr(result, range.getBegin(), range.getEnd())); |
195 replacements_->insert( | 232 } |
196 Replacement(*result.SourceManager, range, replacement_text)); | |
197 } | 233 } |
198 | 234 |
199 } // namespace | 235 } // namespace |
200 | 236 |
201 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 237 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
202 | 238 |
203 int main(int argc, const char* argv[]) { | 239 int main(int argc, const char* argv[]) { |
204 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); | 240 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); |
205 CommonOptionsParser options(argc, argv, category); | 241 CommonOptionsParser options(argc, argv, category); |
206 clang::tooling::ClangTool tool(options.getCompilations(), | 242 clang::tooling::ClangTool tool(options.getCompilations(), |
207 options.getSourcePathList()); | 243 options.getSourcePathList()); |
208 | 244 |
209 MatchFinder match_finder; | 245 MatchFinder match_finder; |
210 Replacements replacements; | 246 Replacements replacements; |
211 | 247 |
212 // Finds all calls to conversion operator member function. This catches calls | 248 // Finds all calls to conversion operator member function. This catches calls |
213 // to "operator T*", "operator Testable", and "operator bool" equally. | 249 // to "operator T*", "operator Testable", and "operator bool" equally. |
214 auto base_matcher = memberCallExpr( | 250 auto base_matcher = |
215 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | 251 id("call", |
216 isTemplateInstantiation())), | 252 memberCallExpr( |
217 callee(conversionDecl())); | 253 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), |
254 isTemplateInstantiation())), | |
255 callee(conversionDecl()), | |
256 on(id("arg", expr())))); | |
218 | 257 |
219 // The heuristic for whether or not a conversion is 'unsafe'. An unsafe | 258 // The heuristic for whether or not converting a temporary is 'unsafe'. An |
220 // conversion is one where a temporary scoped_refptr<T> is converted to | 259 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to |
221 // another type. The matcher provides an exception for a temporary | 260 // 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 | 261 // 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 | 262 // 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 | 263 // retains the necessary reference, since this is a common idiom to see in |
225 // loop bodies. | 264 // loop bodies. |
226 auto is_unsafe_conversion = | 265 auto is_unsafe_temporary_conversion = |
227 bindTemporaryExpr(unless(has(operatorCallExpr()))); | 266 on(bindTemporaryExpr(unless(has(operatorCallExpr())))); |
228 | 267 |
229 auto safe_conversion_matcher = memberCallExpr( | 268 // Returning a scoped_refptr<T> as a T* is considered unsafe if either are |
230 base_matcher, on(id("arg", expr(unless(is_unsafe_conversion))))); | 269 // true: |
231 | 270 // - The scoped_refptr<T> is a temporary. |
232 auto unsafe_conversion_matcher = | 271 // - The scoped_refptr<T> has local lifetime. |
233 memberCallExpr(base_matcher, on(id("arg", is_unsafe_conversion))); | 272 auto returned_as_raw_ptr = hasParent( |
273 returnStmt(hasAncestor(id("fn", functionDecl(returns(pointerType())))))); | |
274 // This matcher intentionally matches more than it should. The matcher | |
275 // callback filters out VarDecls that aren't a scoped_refptr<T>. | |
Ryan Sleevi
2014/08/14 21:26:39
Example of what it matches that it shouldn't?
dcheng
2014/08/14 21:40:12
Done.
| |
276 auto is_local_variable = | |
277 on(declRefExpr(to(id("var", varDecl(hasLocalStorage()))))); | |
278 auto is_unsafe_return = | |
279 anyOf(allOf(hasParent(implicitCastExpr(returned_as_raw_ptr)), | |
280 is_local_variable), | |
281 allOf(hasParent(implicitCastExpr( | |
282 hasParent(exprWithCleanups(returned_as_raw_ptr)))), | |
283 is_unsafe_temporary_conversion)); | |
234 | 284 |
235 // This catches both user-defined conversions (eg: "operator bool") and | 285 // 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 | 286 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a |
237 // pointer to a bool. | 287 // pointer to a bool. |
238 auto implicit_to_bool = | 288 auto implicit_to_bool = |
239 implicitCastExpr(hasImplicitDestinationType(isBoolean())); | 289 implicitCastExpr(hasImplicitDestinationType(isBoolean())); |
240 | 290 |
241 // Avoid converting calls to of "operator Testable" -> "bool" and calls of | 291 // Avoid converting calls to of "operator Testable" -> "bool" and calls of |
242 // "operator T*" -> "bool". | 292 // "operator T*" -> "bool". |
243 auto bool_conversion_matcher = hasParent( | 293 auto bool_conversion_matcher = hasParent( |
244 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); | 294 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); |
245 | 295 |
246 // Find all calls to an operator overload that do NOT (ultimately) result in | 296 // Find all calls to an operator overload that are 'safe'. |
247 // being cast to a bool - eg: where it's being converted to T* and rewrite | |
248 // them to add a call to get(). | |
249 // | 297 // |
250 // All bool conversions will be handled with the Testable trick, but that | 298 // 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 | 299 // can only be used once "operator T*" is removed, since otherwise it leaves |
252 // the call ambiguous. | 300 // the call ambiguous. |
253 GetRewriterCallback get_callback(&replacements); | 301 GetRewriterCallback get_callback(&replacements); |
254 match_finder.addMatcher(id("call", safe_conversion_matcher), &get_callback); | 302 match_finder.addMatcher( |
303 memberCallExpr( | |
304 base_matcher, | |
305 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return))), | |
306 &get_callback); | |
255 | 307 |
256 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. | 308 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. |
257 VarRewriterCallback var_callback(&replacements); | 309 VarRewriterCallback var_callback(&replacements); |
310 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( | |
311 has(memberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); | |
312 match_finder.addMatcher(id("var", | |
313 varDecl(hasInitializer(initialized_with_temporary), | |
314 hasType(pointerType()))), | |
315 &var_callback); | |
258 match_finder.addMatcher( | 316 match_finder.addMatcher( |
259 id("var", | 317 constructorDecl(forEachConstructorInitializer( |
260 varDecl(hasInitializer(ignoringImpCasts(exprWithCleanups( | 318 allOf(withInitializer(initialized_with_temporary), |
261 has(id("call", unsafe_conversion_matcher))))), | 319 forField(id("var", fieldDecl(hasType(pointerType()))))))), |
262 hasType(pointerType()))), | |
263 &var_callback); | 320 &var_callback); |
264 match_finder.addMatcher( | 321 |
265 constructorDecl(forEachConstructorInitializer(allOf( | 322 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when |
266 withInitializer(ignoringImpCasts( | 323 // returning a value. |
267 exprWithCleanups(has(id("call", unsafe_conversion_matcher))))), | 324 FunctionRewriterCallback fn_callback(&replacements); |
268 forField(id("var", fieldDecl(hasType(pointerType()))))))), | 325 match_finder.addMatcher(memberCallExpr(base_matcher, is_unsafe_return), |
269 &var_callback); | 326 &fn_callback); |
270 | 327 |
271 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = | 328 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
272 clang::tooling::newFrontendActionFactory(&match_finder); | 329 clang::tooling::newFrontendActionFactory(&match_finder); |
273 int result = tool.run(factory.get()); | 330 int result = tool.run(factory.get()); |
274 if (result != 0) | 331 if (result != 0) |
275 return result; | 332 return result; |
276 | 333 |
277 // Serialization format is documented in tools/clang/scripts/run_tool.py | 334 // Serialization format is documented in tools/clang/scripts/run_tool.py |
278 llvm::outs() << "==== BEGIN EDITS ====\n"; | 335 llvm::outs() << "==== BEGIN EDITS ====\n"; |
279 for (const auto& r : replacements) { | 336 for (const auto& r : replacements) { |
280 std::string replacement_text = r.getReplacementText().str(); | 337 std::string replacement_text = r.getReplacementText().str(); |
281 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 338 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
282 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" | 339 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" |
283 << r.getLength() << ":" << replacement_text << "\n"; | 340 << r.getLength() << ":" << replacement_text << "\n"; |
284 } | 341 } |
285 llvm::outs() << "==== END EDITS ====\n"; | 342 llvm::outs() << "==== END EDITS ====\n"; |
286 | 343 |
287 return 0; | 344 return 0; |
288 } | 345 } |
OLD | NEW |