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, e.g.: |
181 result.SourceManager->getSpellingLoc(tsi->getTypeLoc().getEndLoc())); | 197 // |
182 if (!range.isValid()) | 198 // in .h: |
| 199 // Foo* my_global_magical_foo; |
| 200 // |
| 201 // in .cc: |
| 202 // Foo* my_global_magical_foo = CreateFoo(); |
| 203 // |
| 204 // In this case, it will only rewrite the .cc definition. Oh well. This should |
| 205 // be rare enough that these cases can be manually handled, since the style |
| 206 // guide prohibits globals of non-POD type. |
| 207 replacements_->insert(RewriteRawPtrToScopedRefptr( |
| 208 result, tsi->getTypeLoc().getBeginLoc(), tsi->getTypeLoc().getEndLoc())); |
| 209 } |
| 210 |
| 211 class FunctionRewriterCallback : public MatchFinder::MatchCallback { |
| 212 public: |
| 213 explicit FunctionRewriterCallback(Replacements* replacements) |
| 214 : replacements_(replacements) {} |
| 215 virtual void run(const MatchFinder::MatchResult& result) override; |
| 216 |
| 217 private: |
| 218 Replacements* const replacements_; |
| 219 }; |
| 220 |
| 221 void FunctionRewriterCallback::run(const MatchFinder::MatchResult& result) { |
| 222 const clang::FunctionDecl* const function_decl = |
| 223 result.Nodes.getNodeAs<clang::FunctionDecl>("fn"); |
| 224 |
| 225 if (!function_decl) |
183 return; | 226 return; |
184 | 227 |
185 std::string text = clang::Lexer::getSourceText( | 228 // If matched against an implicit conversion to a DeclRefExpr, make sure the |
186 range, *result.SourceManager, result.Context->getLangOpts()); | 229 // referenced declaration is of class type, e.g. the tool skips trying to |
187 if (text.empty()) | 230 // chase pointers/references to determine if the pointee is a scoped_refptr<T> |
| 231 // with local storage. Instead, let a human manually handle those cases. |
| 232 const clang::VarDecl* const var_decl = |
| 233 result.Nodes.getNodeAs<clang::VarDecl>("var"); |
| 234 if (var_decl && !var_decl->getTypeSourceInfo()->getType()->isClassType()) { |
188 return; | 235 return; |
189 text.erase(text.rfind('*')); | 236 } |
190 | 237 |
191 std::string replacement_text("scoped_refptr<"); | 238 for (clang::FunctionDecl* f : function_decl->redecls()) { |
192 replacement_text += text; | 239 clang::SourceRange range = f->getReturnTypeSourceRange(); |
193 replacement_text += ">"; | 240 replacements_->insert( |
194 | 241 RewriteRawPtrToScopedRefptr(result, range.getBegin(), range.getEnd())); |
195 replacements_->insert( | 242 } |
196 Replacement(*result.SourceManager, range, replacement_text)); | |
197 } | 243 } |
198 | 244 |
199 } // namespace | 245 } // namespace |
200 | 246 |
201 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 247 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
202 | 248 |
203 int main(int argc, const char* argv[]) { | 249 int main(int argc, const char* argv[]) { |
204 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); | 250 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); |
205 CommonOptionsParser options(argc, argv, category); | 251 CommonOptionsParser options(argc, argv, category); |
206 clang::tooling::ClangTool tool(options.getCompilations(), | 252 clang::tooling::ClangTool tool(options.getCompilations(), |
207 options.getSourcePathList()); | 253 options.getSourcePathList()); |
208 | 254 |
209 MatchFinder match_finder; | 255 MatchFinder match_finder; |
210 Replacements replacements; | 256 Replacements replacements; |
211 | 257 |
212 // Finds all calls to conversion operator member function. This catches calls | 258 // Finds all calls to conversion operator member function. This catches calls |
213 // to "operator T*", "operator Testable", and "operator bool" equally. | 259 // to "operator T*", "operator Testable", and "operator bool" equally. |
214 auto base_matcher = memberCallExpr( | 260 auto base_matcher = |
215 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | 261 id("call", |
216 isTemplateInstantiation())), | 262 memberCallExpr( |
217 callee(conversionDecl())); | 263 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), |
| 264 isTemplateInstantiation())), |
| 265 callee(conversionDecl()), |
| 266 on(id("arg", expr())))); |
218 | 267 |
219 // The heuristic for whether or not a conversion is 'unsafe'. An unsafe | 268 // 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 | 269 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to |
221 // another type. The matcher provides an exception for a temporary | 270 // 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 | 271 // 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 | 272 // 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 | 273 // retains the necessary reference, since this is a common idiom to see in |
225 // loop bodies. | 274 // loop bodies. |
226 auto is_unsafe_conversion = | 275 auto is_unsafe_temporary_conversion = |
227 bindTemporaryExpr(unless(has(operatorCallExpr()))); | 276 on(bindTemporaryExpr(unless(has(operatorCallExpr())))); |
228 | 277 |
229 auto safe_conversion_matcher = memberCallExpr( | 278 // 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))))); | 279 // true: |
231 | 280 // - The scoped_refptr<T> is a temporary. |
232 auto unsafe_conversion_matcher = | 281 // - The scoped_refptr<T> has local lifetime. |
233 memberCallExpr(base_matcher, on(id("arg", is_unsafe_conversion))); | 282 auto returned_as_raw_ptr = hasParent( |
| 283 returnStmt(hasAncestor(id("fn", functionDecl(returns(pointerType())))))); |
| 284 // This matcher intentionally matches more than it should. For example, this |
| 285 // will match: |
| 286 // scoped_refptr<Foo>& foo = some_other_foo; |
| 287 // return foo; |
| 288 // The matcher callback filters out VarDecls that aren't a scoped_refptr<T>, |
| 289 // so those cases can be manually handled. |
| 290 auto is_local_variable = |
| 291 on(declRefExpr(to(id("var", varDecl(hasLocalStorage()))))); |
| 292 auto is_unsafe_return = |
| 293 anyOf(allOf(hasParent(implicitCastExpr(returned_as_raw_ptr)), |
| 294 is_local_variable), |
| 295 allOf(hasParent(implicitCastExpr( |
| 296 hasParent(exprWithCleanups(returned_as_raw_ptr)))), |
| 297 is_unsafe_temporary_conversion)); |
234 | 298 |
235 // This catches both user-defined conversions (eg: "operator bool") and | 299 // 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 | 300 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a |
237 // pointer to a bool. | 301 // pointer to a bool. |
238 auto implicit_to_bool = | 302 auto implicit_to_bool = |
239 implicitCastExpr(hasImplicitDestinationType(isBoolean())); | 303 implicitCastExpr(hasImplicitDestinationType(isBoolean())); |
240 | 304 |
241 // Avoid converting calls to of "operator Testable" -> "bool" and calls of | 305 // Avoid converting calls to of "operator Testable" -> "bool" and calls of |
242 // "operator T*" -> "bool". | 306 // "operator T*" -> "bool". |
243 auto bool_conversion_matcher = hasParent( | 307 auto bool_conversion_matcher = hasParent( |
244 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); | 308 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); |
245 | 309 |
246 // Find all calls to an operator overload that do NOT (ultimately) result in | 310 // 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 // | 311 // |
250 // All bool conversions will be handled with the Testable trick, but that | 312 // 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 | 313 // can only be used once "operator T*" is removed, since otherwise it leaves |
252 // the call ambiguous. | 314 // the call ambiguous. |
253 GetRewriterCallback get_callback(&replacements); | 315 GetRewriterCallback get_callback(&replacements); |
254 match_finder.addMatcher(id("call", safe_conversion_matcher), &get_callback); | 316 match_finder.addMatcher( |
| 317 memberCallExpr( |
| 318 base_matcher, |
| 319 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return))), |
| 320 &get_callback); |
255 | 321 |
256 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. | 322 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. |
257 VarRewriterCallback var_callback(&replacements); | 323 VarRewriterCallback var_callback(&replacements); |
| 324 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( |
| 325 has(memberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); |
| 326 match_finder.addMatcher(id("var", |
| 327 varDecl(hasInitializer(initialized_with_temporary), |
| 328 hasType(pointerType()))), |
| 329 &var_callback); |
258 match_finder.addMatcher( | 330 match_finder.addMatcher( |
259 id("var", | 331 constructorDecl(forEachConstructorInitializer( |
260 varDecl(hasInitializer(ignoringImpCasts(exprWithCleanups( | 332 allOf(withInitializer(initialized_with_temporary), |
261 has(id("call", unsafe_conversion_matcher))))), | 333 forField(id("var", fieldDecl(hasType(pointerType()))))))), |
262 hasType(pointerType()))), | |
263 &var_callback); | 334 &var_callback); |
264 match_finder.addMatcher( | 335 |
265 constructorDecl(forEachConstructorInitializer(allOf( | 336 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when |
266 withInitializer(ignoringImpCasts( | 337 // returning a value. |
267 exprWithCleanups(has(id("call", unsafe_conversion_matcher))))), | 338 FunctionRewriterCallback fn_callback(&replacements); |
268 forField(id("var", fieldDecl(hasType(pointerType()))))))), | 339 match_finder.addMatcher(memberCallExpr(base_matcher, is_unsafe_return), |
269 &var_callback); | 340 &fn_callback); |
270 | 341 |
271 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = | 342 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
272 clang::tooling::newFrontendActionFactory(&match_finder); | 343 clang::tooling::newFrontendActionFactory(&match_finder); |
273 int result = tool.run(factory.get()); | 344 int result = tool.run(factory.get()); |
274 if (result != 0) | 345 if (result != 0) |
275 return result; | 346 return result; |
276 | 347 |
277 // Serialization format is documented in tools/clang/scripts/run_tool.py | 348 // Serialization format is documented in tools/clang/scripts/run_tool.py |
278 llvm::outs() << "==== BEGIN EDITS ====\n"; | 349 llvm::outs() << "==== BEGIN EDITS ====\n"; |
279 for (const auto& r : replacements) { | 350 for (const auto& r : replacements) { |
280 std::string replacement_text = r.getReplacementText().str(); | 351 std::string replacement_text = r.getReplacementText().str(); |
281 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 352 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
282 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" | 353 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" |
283 << r.getLength() << ":" << replacement_text << "\n"; | 354 << r.getLength() << ":" << replacement_text << "\n"; |
284 } | 355 } |
285 llvm::outs() << "==== END EDITS ====\n"; | 356 llvm::outs() << "==== END EDITS ====\n"; |
286 | 357 |
287 return 0; | 358 return 0; |
288 } | 359 } |
OLD | NEW |