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

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: Cleanup 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. 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 }
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