Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(831)

Side by Side Diff: tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp

Issue 472923002: Add logic for catching unsafe scoped_refptr conversions to T* in returns (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/clang/rewrite_scoped_refptr/tests/local-returned-as-raw-expected.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | tools/clang/rewrite_scoped_refptr/tests/local-returned-as-raw-expected.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698