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 |