Chromium Code Reviews| 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 |