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 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 148 // The only remaining calls should be non-dereferencing calls (eg: member | 148 // The only remaining calls should be non-dereferencing calls (eg: member |
| 149 // calls), so a simple ".get()" appending should suffice. | 149 // calls), so a simple ".get()" appending should suffice. |
| 150 if (NeedsParens(arg)) { | 150 if (NeedsParens(arg)) { |
| 151 text.insert(0, "("); | 151 text.insert(0, "("); |
| 152 text.append(")"); | 152 text.append(")"); |
| 153 } | 153 } |
| 154 text.append(".get()"); | 154 text.append(".get()"); |
| 155 replacements_->insert(Replacement(*result.SourceManager, range, text)); | 155 replacements_->insert(Replacement(*result.SourceManager, range, text)); |
| 156 } | 156 } |
| 157 | 157 |
| 158 class VarRewriterCallback : public MatchFinder::MatchCallback { | |
| 159 public: | |
| 160 explicit VarRewriterCallback(Replacements* replacements) | |
| 161 : replacements_(replacements) {} | |
| 162 virtual void run(const MatchFinder::MatchResult& result) override; | |
| 163 | |
| 164 private: | |
| 165 Replacements* const replacements_; | |
| 166 }; | |
| 167 | |
| 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"); | |
| 173 | |
| 174 if (!implicit_call || !var_decl) | |
| 175 return; | |
| 176 | |
| 177 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo(); | |
| 178 | |
| 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()) | |
| 183 return; | |
| 184 | |
| 185 std::string text = clang::Lexer::getSourceText( | |
| 186 range, *result.SourceManager, result.Context->getLangOpts()); | |
| 187 if (text.empty()) | |
| 188 return; | |
| 189 text.erase(text.rfind('*')); | |
| 190 | |
| 191 std::string replacement_text("scoped_refptr<"); | |
| 192 replacement_text += text; | |
| 193 replacement_text += ">"; | |
|
Ryan Sleevi
2014/08/08 22:10:13
No IWYU implications?
dcheng
2014/08/09 21:00:21
If a function declaration is returning a scoped_re
| |
| 194 | |
| 195 replacements_->insert( | |
| 196 Replacement(*result.SourceManager, range, replacement_text)); | |
| 197 } | |
| 198 | |
| 158 } // namespace | 199 } // namespace |
| 159 | 200 |
| 160 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 201 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
| 161 | 202 |
| 162 int main(int argc, const char* argv[]) { | 203 int main(int argc, const char* argv[]) { |
| 163 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); | 204 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); |
| 164 CommonOptionsParser options(argc, argv, category); | 205 CommonOptionsParser options(argc, argv, category); |
| 165 clang::tooling::ClangTool tool(options.getCompilations(), | 206 clang::tooling::ClangTool tool(options.getCompilations(), |
| 166 options.getSourcePathList()); | 207 options.getSourcePathList()); |
| 167 | 208 |
| 168 MatchFinder match_finder; | 209 MatchFinder match_finder; |
| 210 Replacements replacements; | |
| 169 | 211 |
| 170 // Finds all calls to conversion operator member function. This catches calls | 212 // Finds all calls to conversion operator member function. This catches calls |
| 171 // to "operator T*", "operator Testable", and "operator bool" equally. | 213 // to "operator T*", "operator Testable", and "operator bool" equally. |
| 172 StatementMatcher overloaded_call_matcher = memberCallExpr( | 214 auto base_matcher = memberCallExpr( |
|
mdempsky
2014/08/08 21:59:28
[Is C++11 allowed already, or are you just taking
dcheng
2014/08/09 21:00:21
As far as I can tell, it's already required to bui
| |
| 173 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | 215 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), |
| 174 isTemplateInstantiation())), | 216 isTemplateInstantiation())), |
| 175 callee(conversionDecl()), | 217 callee(conversionDecl())); |
| 176 on(id("arg", expr()))); | 218 |
| 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 | |
| 225 // loop bodies. | |
| 226 auto is_unsafe_conversion = | |
| 227 bindTemporaryExpr(unless(has(operatorCallExpr()))); | |
| 228 | |
| 229 auto safe_conversion_matcher = memberCallExpr( | |
| 230 base_matcher, on(id("arg", expr(unless(is_unsafe_conversion))))); | |
| 231 | |
| 232 auto unsafe_conversion_matcher = | |
| 233 memberCallExpr(base_matcher, on(id("arg", is_unsafe_conversion))); | |
| 177 | 234 |
| 178 // This catches both user-defined conversions (eg: "operator bool") and | 235 // This catches both user-defined conversions (eg: "operator bool") and |
| 179 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a | 236 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a |
| 180 // pointer to a bool. | 237 // pointer to a bool. |
| 181 StatementMatcher implicit_to_bool = | 238 auto implicit_to_bool = |
| 182 implicitCastExpr(hasImplicitDestinationType(isBoolean())); | 239 implicitCastExpr(hasImplicitDestinationType(isBoolean())); |
| 183 | 240 |
| 184 // Avoid converting calls to of "operator Testable" -> "bool" and calls of | 241 // Avoid converting calls to of "operator Testable" -> "bool" and calls of |
| 185 // "operator T*" -> "bool". | 242 // "operator T*" -> "bool". |
| 186 StatementMatcher bool_conversion_matcher = hasParent(expr( | 243 auto bool_conversion_matcher = hasParent( |
| 187 anyOf(expr(implicit_to_bool), expr(hasParent(expr(implicit_to_bool)))))); | 244 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); |
| 188 | 245 |
| 189 // Find all calls to an operator overload that do NOT (ultimately) result in | 246 // Find all calls to an operator overload that do NOT (ultimately) result in |
| 190 // being cast to a bool - eg: where it's being converted to T* and rewrite | 247 // being cast to a bool - eg: where it's being converted to T* and rewrite |
| 191 // them to add a call to get(). | 248 // them to add a call to get(). |
| 192 // | 249 // |
| 193 // All bool conversions will be handled with the Testable trick, but that | 250 // All bool conversions will be handled with the Testable trick, but that |
| 194 // can only be used once "operator T*" is removed, since otherwise it leaves | 251 // can only be used once "operator T*" is removed, since otherwise it leaves |
| 195 // the call ambiguous. | 252 // the call ambiguous. |
| 196 Replacements get_replacements; | 253 GetRewriterCallback get_callback(&replacements); |
| 197 GetRewriterCallback get_callback(&get_replacements); | 254 match_finder.addMatcher(id("call", safe_conversion_matcher), &get_callback); |
| 198 match_finder.addMatcher(id("call", expr(overloaded_call_matcher)), | |
| 199 &get_callback); | |
| 200 | 255 |
| 201 #if 0 | 256 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. |
| 202 // Finds all temporary scoped_refptr<T>'s being assigned to a T*. Note that | 257 VarRewriterCallback var_callback(&replacements); |
| 203 // this will result in two callbacks--both the above callback to append get() | |
| 204 // and this callback will match. | |
| 205 match_finder.addMatcher( | 258 match_finder.addMatcher( |
| 206 id("var", | 259 id("var", |
| 207 varDecl(hasInitializer(ignoringImpCasts( | 260 varDecl(hasInitializer(ignoringImpCasts(exprWithCleanups( |
| 208 id("call", expr(overloaded_call_matcher)))), | 261 has(id("call", unsafe_conversion_matcher))))), |
| 209 hasType(pointerType()))), | 262 hasType(pointerType()))), |
| 210 &callback); | 263 &var_callback); |
| 211 match_finder.addMatcher( | 264 match_finder.addMatcher( |
| 212 binaryOperator( | 265 constructorDecl(forEachConstructorInitializer(allOf( |
| 213 hasOperatorName("="), | 266 withInitializer(ignoringImpCasts( |
| 214 hasLHS(declRefExpr(to(id("var", varDecl(hasType(pointerType())))))), | 267 exprWithCleanups(has(id("call", unsafe_conversion_matcher))))), |
| 215 hasRHS(ignoringParenImpCasts( | 268 forField(id("var", fieldDecl(hasType(pointerType()))))))), |
| 216 id("call", expr(overloaded_call_matcher))))), | 269 &var_callback); |
| 217 &callback); | |
| 218 #endif | |
| 219 | 270 |
| 220 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = | 271 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
| 221 clang::tooling::newFrontendActionFactory(&match_finder); | 272 clang::tooling::newFrontendActionFactory(&match_finder); |
| 222 int result = tool.run(factory.get()); | 273 int result = tool.run(factory.get()); |
| 223 if (result != 0) | 274 if (result != 0) |
| 224 return result; | 275 return result; |
| 225 | 276 |
| 226 // Serialization format is documented in tools/clang/scripts/run_tool.py | 277 // Serialization format is documented in tools/clang/scripts/run_tool.py |
| 227 llvm::outs() << "==== BEGIN EDITS ====\n"; | 278 llvm::outs() << "==== BEGIN EDITS ====\n"; |
| 228 for (const auto& r : get_replacements) { | 279 for (const auto& r : replacements) { |
| 229 std::string replacement_text = r.getReplacementText().str(); | 280 std::string replacement_text = r.getReplacementText().str(); |
| 230 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 281 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
| 231 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" | 282 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" |
| 232 << r.getLength() << ":" << replacement_text << "\n"; | 283 << r.getLength() << ":" << replacement_text << "\n"; |
| 233 } | 284 } |
| 234 llvm::outs() << "==== END EDITS ====\n"; | 285 llvm::outs() << "==== END EDITS ====\n"; |
| 235 | 286 |
| 236 return 0; | 287 return 0; |
| 237 } | 288 } |
| OLD | NEW |