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 <assert.h> | |
| 9 #include <algorithm> | 10 #include <algorithm> |
| 10 #include <memory> | 11 #include <memory> |
| 11 #include <string> | 12 #include <string> |
| 12 | 13 |
| 13 #include "clang/AST/ASTContext.h" | 14 #include "clang/AST/ASTContext.h" |
| 14 #include "clang/ASTMatchers/ASTMatchers.h" | 15 #include "clang/ASTMatchers/ASTMatchers.h" |
| 15 #include "clang/ASTMatchers/ASTMatchersMacros.h" | 16 #include "clang/ASTMatchers/ASTMatchersMacros.h" |
| 16 #include "clang/ASTMatchers/ASTMatchFinder.h" | 17 #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 17 #include "clang/Basic/SourceManager.h" | 18 #include "clang/Basic/SourceManager.h" |
| 18 #include "clang/Frontend/FrontendActions.h" | 19 #include "clang/Frontend/FrontendActions.h" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 // Calls to an overloaded operator also need parens, except for foo(...) and | 55 // Calls to an overloaded operator also need parens, except for foo(...) and |
| 55 // foo[...] expressions. | 56 // foo[...] expressions. |
| 56 if (const clang::CXXOperatorCallExpr* op = | 57 if (const clang::CXXOperatorCallExpr* op = |
| 57 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { | 58 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { |
| 58 return op->getOperator() != clang::OO_Call && | 59 return op->getOperator() != clang::OO_Call && |
| 59 op->getOperator() != clang::OO_Subscript; | 60 op->getOperator() != clang::OO_Subscript; |
| 60 } | 61 } |
| 61 return false; | 62 return false; |
| 62 } | 63 } |
| 63 | 64 |
| 64 Replacement RewriteRawPtrToScopedRefptr(const MatchFinder::MatchResult& result, | 65 Replacement RewriteImplicitToExplicitConversion( |
| 65 clang::SourceLocation begin, | 66 const MatchFinder::MatchResult& result, |
| 66 clang::SourceLocation end) { | 67 const clang::Expr* expr) { |
| 67 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | 68 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( |
| 68 result.SourceManager->getSpellingLoc(begin), | 69 result.SourceManager->getSpellingLoc(expr->getLocStart()), |
| 69 result.SourceManager->getSpellingLoc(end)); | 70 result.SourceManager->getSpellingLoc(expr->getLocEnd())); |
| 70 | 71 assert(range.isValid() && "Invalid range!"); |
|
dcheng
2014/08/20 07:02:30
This is apparently LLVM style.
| |
| 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 | |
| 82 class GetRewriterCallback : public MatchFinder::MatchCallback { | |
| 83 public: | |
| 84 explicit GetRewriterCallback(Replacements* replacements) | |
| 85 : replacements_(replacements) {} | |
| 86 virtual void run(const MatchFinder::MatchResult& result) override; | |
| 87 | |
| 88 private: | |
| 89 Replacements* const replacements_; | |
| 90 }; | |
| 91 | |
| 92 void GetRewriterCallback::run(const MatchFinder::MatchResult& result) { | |
| 93 const clang::CXXMemberCallExpr* const implicit_call = | |
| 94 result.Nodes.getNodeAs<clang::CXXMemberCallExpr>("call"); | |
| 95 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); | |
| 96 | |
| 97 if (!implicit_call || !arg) | |
| 98 return; | |
| 99 | |
| 100 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
| 101 result.SourceManager->getSpellingLoc(arg->getLocStart()), | |
| 102 result.SourceManager->getSpellingLoc(arg->getLocEnd())); | |
| 103 if (!range.isValid()) | |
| 104 return; // TODO(rsleevi): Log an error? | |
| 105 | 72 |
| 106 // Handle cases where an implicit cast is being done by dereferencing a | 73 // Handle cases where an implicit cast is being done by dereferencing a |
| 107 // pointer to a scoped_refptr<> (sadly, it happens...) | 74 // pointer to a scoped_refptr<> (sadly, it happens...) |
| 108 // | 75 // |
| 109 // This rewrites both "*foo" and "*(foo)" as "foo->get()". | 76 // This rewrites both "*foo" and "*(foo)" as "foo->get()". |
| 110 if (const clang::UnaryOperator* op = | 77 if (const clang::UnaryOperator* op = |
| 111 llvm::dyn_cast<clang::UnaryOperator>(arg)) { | 78 llvm::dyn_cast<clang::UnaryOperator>(expr)) { |
| 112 if (op->getOpcode() == clang::UO_Deref) { | 79 if (op->getOpcode() == clang::UO_Deref) { |
| 113 const clang::Expr* const sub_expr = | 80 const clang::Expr* const sub_expr = |
| 114 op->getSubExpr()->IgnoreParenImpCasts(); | 81 op->getSubExpr()->IgnoreParenImpCasts(); |
| 115 clang::CharSourceRange sub_expr_range = | 82 clang::CharSourceRange sub_expr_range = |
| 116 clang::CharSourceRange::getTokenRange( | 83 clang::CharSourceRange::getTokenRange( |
| 117 result.SourceManager->getSpellingLoc(sub_expr->getLocStart()), | 84 result.SourceManager->getSpellingLoc(sub_expr->getLocStart()), |
| 118 result.SourceManager->getSpellingLoc(sub_expr->getLocEnd())); | 85 result.SourceManager->getSpellingLoc(sub_expr->getLocEnd())); |
| 119 if (!sub_expr_range.isValid()) | 86 assert(sub_expr_range.isValid() && "Invalid subexpression range!"); |
| 120 return; // TODO(rsleevi): Log an error? | 87 |
| 121 std::string inner_text = clang::Lexer::getSourceText( | 88 std::string inner_text = clang::Lexer::getSourceText( |
| 122 sub_expr_range, *result.SourceManager, result.Context->getLangOpts()); | 89 sub_expr_range, *result.SourceManager, result.Context->getLangOpts()); |
| 123 if (inner_text.empty()) | 90 assert(!inner_text.empty() && "No text for subexpression!"); |
| 124 return; // TODO(rsleevi): Log an error? | |
| 125 | |
| 126 if (NeedsParens(sub_expr)) { | 91 if (NeedsParens(sub_expr)) { |
| 127 inner_text.insert(0, "("); | 92 inner_text.insert(0, "("); |
| 128 inner_text.append(")"); | 93 inner_text.append(")"); |
| 129 } | 94 } |
| 130 inner_text.append("->get()"); | 95 inner_text.append("->get()"); |
| 131 replacements_->insert( | 96 return Replacement(*result.SourceManager, range, inner_text); |
| 132 Replacement(*result.SourceManager, range, inner_text)); | |
| 133 return; | |
| 134 } | 97 } |
| 135 } | 98 } |
| 136 | 99 |
| 137 std::string text = clang::Lexer::getSourceText( | 100 std::string text = clang::Lexer::getSourceText( |
| 138 range, *result.SourceManager, result.Context->getLangOpts()); | 101 range, *result.SourceManager, result.Context->getLangOpts()); |
| 139 if (text.empty()) | 102 assert(!text.empty() && "No text for expression!"); |
| 140 return; // TODO(rsleevi): Log an error? | |
| 141 | 103 |
| 142 // Unwrap any temporaries - for example, custom iterators that return | 104 // Unwrap any temporaries - for example, custom iterators that return |
| 143 // scoped_refptr<T> as part of operator*. Any such iterators should also | 105 // scoped_refptr<T> as part of operator*. Any such iterators should also |
| 144 // be declaring a scoped_refptr<T>* operator->, per C++03 24.4.1.1 (Table 72) | 106 // be declaring a scoped_refptr<T>* operator->, per C++03 24.4.1.1 (Table 72) |
| 145 if (const clang::CXXBindTemporaryExpr* op = | 107 if (const clang::CXXBindTemporaryExpr* op = |
| 146 llvm::dyn_cast<clang::CXXBindTemporaryExpr>(arg)) { | 108 llvm::dyn_cast<clang::CXXBindTemporaryExpr>(expr)) { |
| 147 arg = op->getSubExpr(); | 109 expr = op->getSubExpr(); |
| 148 } | 110 } |
| 149 | 111 |
| 150 // Handle iterators (which are operator* calls, followed by implicit | 112 // Handle iterators (which are operator* calls, followed by implicit |
| 151 // conversions) by rewriting *it as it->get() | 113 // conversions) by rewriting *it as it->get() |
| 152 if (const clang::CXXOperatorCallExpr* op = | 114 if (const clang::CXXOperatorCallExpr* op = |
| 153 llvm::dyn_cast<clang::CXXOperatorCallExpr>(arg)) { | 115 llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) { |
| 154 if (op->getOperator() == clang::OO_Star) { | 116 if (op->getOperator() == clang::OO_Star) { |
| 155 // Note that this doesn't rewrite **it correctly, since it should be | 117 // Note that this doesn't rewrite **it correctly, since it should be |
| 156 // rewritten using parens, e.g. (*it)->get(). However, this shouldn't | 118 // rewritten using parens, e.g. (*it)->get(). However, this shouldn't |
| 157 // happen frequently, if at all, since it would likely indicate code is | 119 // happen frequently, if at all, since it would likely indicate code is |
| 158 // storing pointers to a scoped_refptr in a container. | 120 // storing pointers to a scoped_refptr in a container. |
| 159 text.erase(0, 1); | 121 text.erase(0, 1); |
| 160 text.append("->get()"); | 122 text.append("->get()"); |
| 161 replacements_->insert(Replacement(*result.SourceManager, range, text)); | 123 return Replacement(*result.SourceManager, range, text); |
| 162 return; | |
| 163 } | 124 } |
| 164 } | 125 } |
| 165 | 126 |
| 166 // The only remaining calls should be non-dereferencing calls (eg: member | 127 // The only remaining calls should be non-dereferencing calls (eg: member |
| 167 // calls), so a simple ".get()" appending should suffice. | 128 // calls), so a simple ".get()" appending should suffice. |
| 168 if (NeedsParens(arg)) { | 129 if (NeedsParens(expr)) { |
| 169 text.insert(0, "("); | 130 text.insert(0, "("); |
| 170 text.append(")"); | 131 text.append(")"); |
| 171 } | 132 } |
| 172 text.append(".get()"); | 133 text.append(".get()"); |
| 173 replacements_->insert(Replacement(*result.SourceManager, range, text)); | 134 return Replacement(*result.SourceManager, range, text); |
| 135 } | |
| 136 | |
| 137 Replacement RewriteRawPtrToScopedRefptr(const MatchFinder::MatchResult& result, | |
|
dcheng
2014/08/20 07:02:30
This is just a move of the original code, re-addin
| |
| 138 clang::SourceLocation begin, | |
| 139 clang::SourceLocation end) { | |
| 140 clang::CharSourceRange range = clang::CharSourceRange::getTokenRange( | |
| 141 result.SourceManager->getSpellingLoc(begin), | |
| 142 result.SourceManager->getSpellingLoc(end)); | |
| 143 assert(range.isValid() && "Invalid range!"); | |
| 144 | |
| 145 std::string text = clang::Lexer::getSourceText( | |
| 146 range, *result.SourceManager, result.Context->getLangOpts()); | |
| 147 text.erase(text.rfind('*')); | |
| 148 | |
| 149 std::string replacement_text("scoped_refptr<"); | |
| 150 replacement_text += text; | |
| 151 replacement_text += ">"; | |
| 152 | |
| 153 return Replacement(*result.SourceManager, range, replacement_text); | |
| 154 } | |
| 155 | |
| 156 class GetRewriterCallback : public MatchFinder::MatchCallback { | |
| 157 public: | |
| 158 explicit GetRewriterCallback(Replacements* replacements) | |
| 159 : replacements_(replacements) {} | |
| 160 virtual void run(const MatchFinder::MatchResult& result) override; | |
| 161 | |
| 162 private: | |
| 163 Replacements* const replacements_; | |
| 164 }; | |
| 165 | |
| 166 void GetRewriterCallback::run(const MatchFinder::MatchResult& result) { | |
| 167 const clang::Expr* arg = result.Nodes.getNodeAs<clang::Expr>("arg"); | |
| 168 assert(arg && "Unexpected match! No Expr captured!"); | |
| 169 replacements_->insert(RewriteImplicitToExplicitConversion(result, arg)); | |
| 174 } | 170 } |
| 175 | 171 |
| 176 class VarRewriterCallback : public MatchFinder::MatchCallback { | 172 class VarRewriterCallback : public MatchFinder::MatchCallback { |
| 177 public: | 173 public: |
| 178 explicit VarRewriterCallback(Replacements* replacements) | 174 explicit VarRewriterCallback(Replacements* replacements) |
| 179 : replacements_(replacements) {} | 175 : replacements_(replacements) {} |
| 180 virtual void run(const MatchFinder::MatchResult& result) override; | 176 virtual void run(const MatchFinder::MatchResult& result) override; |
| 181 | 177 |
| 182 private: | 178 private: |
| 183 Replacements* const replacements_; | 179 Replacements* const replacements_; |
| 184 }; | 180 }; |
| 185 | 181 |
| 186 void VarRewriterCallback::run(const MatchFinder::MatchResult& result) { | 182 void VarRewriterCallback::run(const MatchFinder::MatchResult& result) { |
| 187 const clang::DeclaratorDecl* const var_decl = | 183 const clang::DeclaratorDecl* const var_decl = |
| 188 result.Nodes.getNodeAs<clang::DeclaratorDecl>("var"); | 184 result.Nodes.getNodeAs<clang::DeclaratorDecl>("var"); |
| 189 | 185 assert(var_decl && "Unexpected match! No VarDecl captured!"); |
| 190 if (!var_decl) | |
| 191 return; | |
| 192 | 186 |
| 193 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo(); | 187 const clang::TypeSourceInfo* tsi = var_decl->getTypeSourceInfo(); |
| 194 | 188 |
| 195 // TODO(dcheng): This mishandles a case where a variable has multiple | 189 // TODO(dcheng): This mishandles a case where a variable has multiple |
| 196 // declarations, e.g.: | 190 // declarations, e.g.: |
| 197 // | 191 // |
| 198 // in .h: | 192 // in .h: |
| 199 // Foo* my_global_magical_foo; | 193 // Foo* my_global_magical_foo; |
| 200 // | 194 // |
| 201 // in .cc: | 195 // in .cc: |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 214 : replacements_(replacements) {} | 208 : replacements_(replacements) {} |
| 215 virtual void run(const MatchFinder::MatchResult& result) override; | 209 virtual void run(const MatchFinder::MatchResult& result) override; |
| 216 | 210 |
| 217 private: | 211 private: |
| 218 Replacements* const replacements_; | 212 Replacements* const replacements_; |
| 219 }; | 213 }; |
| 220 | 214 |
| 221 void FunctionRewriterCallback::run(const MatchFinder::MatchResult& result) { | 215 void FunctionRewriterCallback::run(const MatchFinder::MatchResult& result) { |
| 222 const clang::FunctionDecl* const function_decl = | 216 const clang::FunctionDecl* const function_decl = |
| 223 result.Nodes.getNodeAs<clang::FunctionDecl>("fn"); | 217 result.Nodes.getNodeAs<clang::FunctionDecl>("fn"); |
| 224 | 218 assert(function_decl && "Unexpected match! No FunctionDecl captured!"); |
| 225 if (!function_decl) | |
| 226 return; | |
| 227 | 219 |
| 228 // If matched against an implicit conversion to a DeclRefExpr, make sure the | 220 // If matched against an implicit conversion to a DeclRefExpr, make sure the |
| 229 // referenced declaration is of class type, e.g. the tool skips trying to | 221 // referenced declaration is of class type, e.g. the tool skips trying to |
| 230 // chase pointers/references to determine if the pointee is a scoped_refptr<T> | 222 // 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. | 223 // with local storage. Instead, let a human manually handle those cases. |
| 232 const clang::VarDecl* const var_decl = | 224 const clang::VarDecl* const var_decl = |
| 233 result.Nodes.getNodeAs<clang::VarDecl>("var"); | 225 result.Nodes.getNodeAs<clang::VarDecl>("var"); |
| 234 if (var_decl && !var_decl->getTypeSourceInfo()->getType()->isClassType()) { | 226 if (var_decl && !var_decl->getTypeSourceInfo()->getType()->isClassType()) { |
| 235 return; | 227 return; |
| 236 } | 228 } |
| 237 | 229 |
| 238 for (clang::FunctionDecl* f : function_decl->redecls()) { | 230 for (clang::FunctionDecl* f : function_decl->redecls()) { |
| 239 clang::SourceRange range = f->getReturnTypeSourceRange(); | 231 clang::SourceRange range = f->getReturnTypeSourceRange(); |
| 240 replacements_->insert( | 232 replacements_->insert( |
| 241 RewriteRawPtrToScopedRefptr(result, range.getBegin(), range.getEnd())); | 233 RewriteRawPtrToScopedRefptr(result, range.getBegin(), range.getEnd())); |
| 242 } | 234 } |
| 243 } | 235 } |
| 244 | 236 |
| 237 class MacroRewriterCallback : public MatchFinder::MatchCallback { | |
| 238 public: | |
| 239 explicit MacroRewriterCallback(Replacements* replacements) | |
| 240 : replacements_(replacements) {} | |
| 241 virtual void run(const MatchFinder::MatchResult& result) override; | |
| 242 | |
| 243 private: | |
| 244 Replacements* const replacements_; | |
| 245 }; | |
| 246 | |
| 247 void MacroRewriterCallback::run(const MatchFinder::MatchResult& result) { | |
| 248 const clang::Expr* const expr = result.Nodes.getNodeAs<clang::Expr>("expr"); | |
| 249 assert(expr && "Unexpected match! No Expr captured!"); | |
| 250 replacements_->insert(RewriteImplicitToExplicitConversion(result, expr)); | |
| 251 } | |
| 252 | |
| 245 } // namespace | 253 } // namespace |
| 246 | 254 |
| 247 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 255 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
| 248 | 256 |
| 249 int main(int argc, const char* argv[]) { | 257 int main(int argc, const char* argv[]) { |
| 250 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); | 258 llvm::cl::OptionCategory category("Remove scoped_refptr conversions"); |
| 251 CommonOptionsParser options(argc, argv, category); | 259 CommonOptionsParser options(argc, argv, category); |
| 252 clang::tooling::ClangTool tool(options.getCompilations(), | 260 clang::tooling::ClangTool tool(options.getCompilations(), |
| 253 options.getSourcePathList()); | 261 options.getSourcePathList()); |
| 254 | 262 |
| 255 MatchFinder match_finder; | 263 MatchFinder match_finder; |
| 256 Replacements replacements; | 264 Replacements replacements; |
| 257 | 265 |
| 266 auto is_scoped_refptr = recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | |
| 267 isTemplateInstantiation()); | |
| 268 | |
| 258 // Finds all calls to conversion operator member function. This catches calls | 269 // Finds all calls to conversion operator member function. This catches calls |
| 259 // to "operator T*", "operator Testable", and "operator bool" equally. | 270 // to "operator T*", "operator Testable", and "operator bool" equally. |
| 260 auto base_matcher = | 271 auto base_matcher = memberCallExpr(thisPointerType(is_scoped_refptr), |
| 261 id("call", | 272 callee(conversionDecl()), |
| 262 memberCallExpr( | 273 on(id("arg", expr()))); |
| 263 thisPointerType(recordDecl(isSameOrDerivedFrom("::scoped_refptr"), | |
| 264 isTemplateInstantiation())), | |
| 265 callee(conversionDecl()), | |
| 266 on(id("arg", expr())))); | |
| 267 | 274 |
| 268 // The heuristic for whether or not converting a temporary is 'unsafe'. An | 275 // The heuristic for whether or not converting a temporary is 'unsafe'. An |
| 269 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to | 276 // unsafe conversion is one where a temporary scoped_refptr<T> is converted to |
| 270 // another type. The matcher provides an exception for a temporary | 277 // another type. The matcher provides an exception for a temporary |
| 271 // scoped_refptr that is the result of an operator call. In this case, assume | 278 // scoped_refptr that is the result of an operator call. In this case, assume |
| 272 // that it's the result of an iterator dereference, and the container itself | 279 // that it's the result of an iterator dereference, and the container itself |
| 273 // retains the necessary reference, since this is a common idiom to see in | 280 // retains the necessary reference, since this is a common idiom to see in |
| 274 // loop bodies. | 281 // loop bodies. |
| 275 auto is_unsafe_temporary_conversion = | 282 auto is_unsafe_temporary_conversion = |
| 276 on(bindTemporaryExpr(unless(has(operatorCallExpr())))); | 283 on(bindTemporaryExpr(unless(has(operatorCallExpr())))); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 300 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a | 307 // standard conversion sequence (C++03 13.3.3.1.1), such as converting a |
| 301 // pointer to a bool. | 308 // pointer to a bool. |
| 302 auto implicit_to_bool = | 309 auto implicit_to_bool = |
| 303 implicitCastExpr(hasImplicitDestinationType(isBoolean())); | 310 implicitCastExpr(hasImplicitDestinationType(isBoolean())); |
| 304 | 311 |
| 305 // Avoid converting calls to of "operator Testable" -> "bool" and calls of | 312 // Avoid converting calls to of "operator Testable" -> "bool" and calls of |
| 306 // "operator T*" -> "bool". | 313 // "operator T*" -> "bool". |
| 307 auto bool_conversion_matcher = hasParent( | 314 auto bool_conversion_matcher = hasParent( |
| 308 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); | 315 expr(anyOf(implicit_to_bool, expr(hasParent(implicit_to_bool))))); |
| 309 | 316 |
| 317 auto is_logging_helper = | |
| 318 functionDecl(anyOf(hasName("CheckEQImpl"), hasName("CheckNEImpl"))); | |
| 319 auto is_gtest_helper = functionDecl( | |
| 320 anyOf(methodDecl(ofClass(recordDecl(isSameOrDerivedFrom( | |
| 321 hasName("::testing::internal::EqHelper")))), | |
| 322 hasName("Compare")), | |
| 323 hasName("::testing::internal::CmpHelperNE"))); | |
| 324 auto is_gtest_assertion_result_ctor = constructorDecl(ofClass( | |
| 325 recordDecl(isSameOrDerivedFrom(hasName("::testing::AssertionResult"))))); | |
| 326 | |
| 310 // Find all calls to an operator overload that are 'safe'. | 327 // Find all calls to an operator overload that are 'safe'. |
| 311 // | 328 // |
| 312 // All bool conversions will be handled with the Testable trick, but that | 329 // All bool conversions will be handled with the Testable trick, but that |
| 313 // can only be used once "operator T*" is removed, since otherwise it leaves | 330 // can only be used once "operator T*" is removed, since otherwise it leaves |
| 314 // the call ambiguous. | 331 // the call ambiguous. |
| 315 GetRewriterCallback get_callback(&replacements); | 332 GetRewriterCallback get_callback(&replacements); |
| 316 match_finder.addMatcher( | 333 match_finder.addMatcher( |
| 317 memberCallExpr( | 334 memberCallExpr( |
| 318 base_matcher, | 335 base_matcher, |
| 319 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return))), | 336 // Excluded since the conversion may be unsafe. |
| 337 unless(anyOf(is_unsafe_temporary_conversion, is_unsafe_return)), | |
| 338 // Excluded since the conversion is deep inside a macro that needs to | |
| 339 // be specially handled. | |
| 340 unless(hasAncestor(decl(anyOf(is_logging_helper, | |
| 341 is_gtest_helper, | |
| 342 is_gtest_assertion_result_ctor))))), | |
| 320 &get_callback); | 343 &get_callback); |
| 321 | 344 |
| 322 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. | 345 // Find temporary scoped_refptr<T>'s being unsafely assigned to a T*. |
| 323 VarRewriterCallback var_callback(&replacements); | 346 VarRewriterCallback var_callback(&replacements); |
| 324 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( | 347 auto initialized_with_temporary = ignoringImpCasts(exprWithCleanups( |
| 325 has(memberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); | 348 has(memberCallExpr(base_matcher, is_unsafe_temporary_conversion)))); |
| 326 match_finder.addMatcher(id("var", | 349 match_finder.addMatcher(id("var", |
| 327 varDecl(hasInitializer(initialized_with_temporary), | 350 varDecl(hasInitializer(initialized_with_temporary), |
| 328 hasType(pointerType()))), | 351 hasType(pointerType()))), |
| 329 &var_callback); | 352 &var_callback); |
| 330 match_finder.addMatcher( | 353 match_finder.addMatcher( |
| 331 constructorDecl(forEachConstructorInitializer( | 354 constructorDecl(forEachConstructorInitializer( |
| 332 allOf(withInitializer(initialized_with_temporary), | 355 allOf(withInitializer(initialized_with_temporary), |
| 333 forField(id("var", fieldDecl(hasType(pointerType()))))))), | 356 forField(id("var", fieldDecl(hasType(pointerType()))))))), |
| 334 &var_callback); | 357 &var_callback); |
| 335 | 358 |
| 336 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when | 359 // Rewrite functions that unsafely turn a scoped_refptr<T> into a T* when |
| 337 // returning a value. | 360 // returning a value. |
| 338 FunctionRewriterCallback fn_callback(&replacements); | 361 FunctionRewriterCallback fn_callback(&replacements); |
| 339 match_finder.addMatcher(memberCallExpr(base_matcher, is_unsafe_return), | 362 match_finder.addMatcher(memberCallExpr(base_matcher, is_unsafe_return), |
| 340 &fn_callback); | 363 &fn_callback); |
| 341 | 364 |
| 365 // Rewrite logging / gtest expressions that result in an implicit conversion. | |
| 366 // Luckily, the matchers don't need to handle the case where one of the macro | |
| 367 // arguments is NULL, such as: | |
| 368 // CHECK_EQ(my_scoped_refptr, NULL) | |
| 369 // because it simply doesn't compile--since NULL is actually of integral type, | |
| 370 // this doesn't trigger scoped_refptr<T>'s implicit conversion. Since there is | |
| 371 // no comparison overload for scoped_refptr<T> and int, this fails to compile. | |
| 372 MacroRewriterCallback macro_callback(&replacements); | |
| 373 // CHECK_EQ/CHECK_NE helpers. | |
| 374 match_finder.addMatcher( | |
| 375 callExpr(callee(is_logging_helper), | |
| 376 argumentCountIs(3), | |
| 377 hasAnyArgument(id("expr", expr(hasType(is_scoped_refptr)))), | |
| 378 hasAnyArgument(hasType(pointerType())), | |
| 379 hasArgument(2, stringLiteral())), | |
| 380 ¯o_callback); | |
| 381 // ASSERT_EQ/ASSERT_NE/EXPECT_EQ/EXPECT_EQ, which use the same underlying | |
| 382 // helper functions. Even though gtest has special handling for pointer to | |
| 383 // NULL comparisons, it doesn't trigger in this case, so no special handling | |
| 384 // is needed for the replacements. | |
| 385 match_finder.addMatcher( | |
| 386 callExpr(callee(is_gtest_helper), | |
| 387 argumentCountIs(4), | |
| 388 hasArgument(0, stringLiteral()), | |
| 389 hasArgument(1, stringLiteral()), | |
| 390 hasAnyArgument(id("expr", expr(hasType(is_scoped_refptr)))), | |
| 391 hasAnyArgument(hasType(pointerType()))), | |
| 392 ¯o_callback); | |
| 393 // ASSERT_TRUE/EXPECT_TRUE helpers. Note that this matcher doesn't need to | |
| 394 // handle ASSERT_FALSE/EXPECT_FALSE, because it gets coerced to bool before | |
| 395 // being passed as an argument to AssertionResult's constructor. As a result, | |
| 396 // GetRewriterCallback handles this case properly since the conversion isn't | |
| 397 // hidden inside AssertionResult, and the generated replacement properly | |
| 398 // rewrites the macro argument. | |
| 399 // However, the tool does need to handle the _TRUE counterparts, since the | |
| 400 // conversion occurs inside the constructor in those cases. | |
| 401 match_finder.addMatcher( | |
| 402 constructExpr( | |
| 403 argumentCountIs(2), | |
| 404 hasArgument(0, id("expr", expr(hasType(is_scoped_refptr)))), | |
| 405 hasDeclaration(is_gtest_assertion_result_ctor)), | |
| 406 ¯o_callback); | |
| 407 | |
| 342 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = | 408 std::unique_ptr<clang::tooling::FrontendActionFactory> factory = |
| 343 clang::tooling::newFrontendActionFactory(&match_finder); | 409 clang::tooling::newFrontendActionFactory(&match_finder); |
| 344 int result = tool.run(factory.get()); | 410 int result = tool.run(factory.get()); |
| 345 if (result != 0) | 411 if (result != 0) |
| 346 return result; | 412 return result; |
| 347 | 413 |
| 348 // Serialization format is documented in tools/clang/scripts/run_tool.py | 414 // Serialization format is documented in tools/clang/scripts/run_tool.py |
| 349 llvm::outs() << "==== BEGIN EDITS ====\n"; | 415 llvm::outs() << "==== BEGIN EDITS ====\n"; |
| 350 for (const auto& r : replacements) { | 416 for (const auto& r : replacements) { |
| 351 std::string replacement_text = r.getReplacementText().str(); | 417 std::string replacement_text = r.getReplacementText().str(); |
| 352 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); | 418 std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); |
| 353 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" | 419 llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" |
| 354 << r.getLength() << ":" << replacement_text << "\n"; | 420 << r.getLength() << ":" << replacement_text << "\n"; |
| 355 } | 421 } |
| 356 llvm::outs() << "==== END EDITS ====\n"; | 422 llvm::outs() << "==== END EDITS ====\n"; |
| 357 | 423 |
| 358 return 0; | 424 return 0; |
| 359 } | 425 } |
| OLD | NEW |