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

Side by Side Diff: tools/clang/BindMigrate/OldCallbackTransform.cpp

Issue 7886056: Clang plugin that rewrites PostTask(_, NewRunnableMethod(...)) to PostTask(_, base::Bind(...)); (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix lifetime issue where BindMigrateAction is deleted before the ASTConsumer is finished. Created 9 years, 2 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
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "OldCallbackTransform.h"
6
7 #include "clang/AST/ASTContext.h"
8 #include "clang/AST/Expr.h"
9 #include "clang/AST/ExprCXX.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Rewrite/Rewriter.h"
12
13 #include "DiagnosticEmitter.h"
14
15 using namespace std;
16
17 namespace {
18
19 std::string RemoveSpaceBeforePtrOrRefInType(const std::string &orig) {
20 if (orig.size() < 2) {
21 return orig;
22 }
23
24 string out;
25 std::string::const_iterator it = orig.begin();
26 std::string::const_iterator end = orig.end();
27 char prev_char = *(it++);
28 for (; it != end; ++it) {
29 // Since it's a type, we know it's not going to be a binary expression, so
30 // we can just look for the single chars and deicde if we should skip the
31 // last char.
32 if (*it != '*' && *it != '&') {
33 out.append(1, prev_char);
34 }
35
36 prev_char = *it;
37 }
38
39 out.append(1, prev_char);
40 return out;
41 }
42
43 } // namespace
44
45 namespace clang {
46
47 OldCallbackTransform::OldCallbackTransform(ASTContext* context,
48 Rewriter* rewriter,
49 DiagnosticEmitter* emitter)
50 : context_(context), rewriter_(rewriter), error_emitter_(emitter) {
51 }
52
53 bool OldCallbackTransform::WouldTransformStmt(Stmt* statement) {
54 return IsOldCallbackGenerator(statement) || IsOldCallbackRun(statement);
55 }
56
57 bool OldCallbackTransform::WouldTransformDecl(Decl* decl) {
58 bool dont_care = false;
59 if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
60 return IsOldCallbackType(td->getUnderlyingType(), &dont_care);
61 } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
62 return IsOldCallbackType(vd->getType(), &dont_care);
63 }
64
65 return false;
66 }
67
68 void OldCallbackTransform::TransformStmt(Stmt *statement) {
69 // Catch all NewCallback() calls.
70 RewriteIfOldCallbackGenerator(statement);
71
72 // Catch all CallbackN::Run() calls.
73 RewriteIfOldCallbackRun(statement);
74 }
75
76 void OldCallbackTransform::TransformDecl(Decl *decl) {
77 // Catch all typedefs and variables/parameter declarations.
78 if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
79 RewriteTypedefIfOldCallback(td);
80 } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
81 RewriteVarIfOldCallback(vd);
82 }
83 }
84
85 void OldCallbackTransform::RewriteIfOldCallbackGenerator(Stmt* statement) {
86 if (!IsOldCallbackGenerator(statement)) {
87 return;
88 }
89
90 // Both callback variants only work with methods. So we always s
91 // NewCallback* -> base::Bind
92 CallExpr* ce = dyn_cast<CallExpr>(statement);
93 if (rewriter_->ReplaceText(ce->getCallee()->getSourceRange(),
94 "base::Bind")) {
95 error_emitter_->EmitError(statement->getLocStart(),
96 "Unable to rewrite to base::Bind()");
97 }
98
99 Expr* arg1 = ce->getArgs()[0];
100 Expr* arg2 = ce->getArgs()[1];
101
102 if (rewriter_->ReplaceStmt(arg1, arg2)) {
103 error_emitter_->EmitError(statement->getLocStart(),
104 "Unable to swap the first argument");
105 }
106
107 std::string unretained_arg("base::Unretained(");
108 unretained_arg += rewriter_->ConvertToString(arg1);
109 unretained_arg += ")";
110 if (rewriter_->ReplaceText(arg2->getSourceRange(), unretained_arg)) {
111 error_emitter_->EmitError(statement->getLocStart(),
112 "Unable to rewrite the second argument");
113 }
114 }
115
116 void OldCallbackTransform::RewriteIfOldCallbackRun(Stmt* statement) {
117 if (!IsOldCallbackRun(statement)) {
118 return;
119 }
120
121 // Grab the statement and do "s/->Run(/.Run(/".
122 SourceManager& source_manager = context_->getSourceManager();
123 std::pair<FileID,unsigned> start = source_manager.getDecomposedLoc(
124 statement->getLocStart());
125 std::pair<FileID,unsigned> end = source_manager.getDecomposedLoc(
126 statement->getLocEnd());
127
128 StringRef buffer = source_manager.getBufferData(start.first);
129 static const char* kOldRun = "->Run(";
130 size_t where = buffer.find(kOldRun, start.second);
131
132
133 if (where > end.second) {
134 // There's no ".Run(" in this statement. Lucky us. Exit early.
135 return;
136 }
137
138 SourceLocation replace_start =
139 source_manager.getLocForStartOfFile(start.first)
140 .getFileLocWithOffset(where);
141 std::pair<FileID,unsigned> rstart_b = source_manager.getDecomposedLoc(
142 replace_start);
143
144 if (rewriter_->ReplaceText(replace_start, strlen(kOldRun), ".Run(")) {
145 error_emitter_->EmitWarning(replace_start,
146 "Unable to rewrite ->Run() -> .Run()");
147 }
148 }
149
150 void OldCallbackTransform::RewriteTypedefIfOldCallback(TypedefDecl* td) {
151 // Skip anything from callback_old.h. That'd be silly.
152 // TODO(ajwong): do this.
153
154 bool is_callback_with_return = false;
155 QualType underlying_type = td->getUnderlyingType();
156 if (!IsOldCallbackType(underlying_type, &is_callback_with_return)) {
157 return;
158 }
159
160 string new_typedef = "typedef ";
161
162 if (!CallbackFromType(underlying_type, td->getLocStart(),
163 is_callback_with_return, &new_typedef)) {
164 // Error emited by CallbackFromType function.
165 return;
166 }
167
168 if (underlying_type->isReferenceType()) {
169 error_emitter_->EmitWarning(
170 td->getLocStart(),
171 "Template to a reference type? That's strange. Skipping.");
172 return;
173 }
174
175 // Add identifier
176 new_typedef += " ";
177 new_typedef += td->getIdentifier()->getName();
178 new_typedef += ";";
179
180 // ?!?!?!?!! WTF DOES THIS RETURN TRUE ON ERROR?!
181 if (rewriter_->ReplaceText(td->getSourceRange(), new_typedef)) {
182 error_emitter_->EmitError(td->getLocStart(), "Unable to rewrite typedef.");
183 }
184 }
185
186 void OldCallbackTransform::RewriteVarIfOldCallback(VarDecl* vd) {
187 bool is_callback_with_return = false;
188 if (!IsOldCallbackType(vd->getType(), &is_callback_with_return)) {
189 return;
190 }
191
192 std::string new_decl;
193 if (!CallbackFromType(vd->getType(), vd->getLocStart(),
194 is_callback_with_return, &new_decl)) {
195 // Error emited by CallbackFromType function.
196 return;
197 }
198
199 new_decl += " ";
200 new_decl += vd->getIdentifier()->getName();
201 if (rewriter_->ReplaceText(vd->getSourceRange(), new_decl)) {
202 error_emitter_->EmitError(vd->getLocStart(), "Unable to rewrite var.");
203 }
204 }
205
206 bool OldCallbackTransform::CallbackFromType(QualType type,
207 SourceLocation loc,
208 bool is_callback_with_return,
209 std::string* output) {
210 string buffer;
211
212 // Rewrite the whole declaration including const, and volatile. Start
213 // constructing here. Make sure to not propagate pointers though.
214 if (type.isConstQualified()) buffer += "const ";
215 if (type.isRestrictQualified()) buffer += "restrict ";
216 if (type.isVolatileQualified()) buffer += "volatile ";
217
218 if (is_callback_with_return) {
219 if (!CallbackFromTypeWithReturn(type, loc, &buffer)) {
220 return false;
221 }
222 } else {
223 if (!CallbackFromVoidReturnType(type, loc, &buffer)) {
224 return false;
225 }
226 }
227
228 if (type->isReferenceType()) buffer += "&";
229
230 output->append(buffer);
231 return true;
232 }
233
234 bool OldCallbackTransform::CallbackFromVoidReturnType(QualType type,
235 SourceLocation loc,
236 std::string* output) {
237 // Grab record.
238 ClassTemplateSpecializationDecl* callback_decl;
239 if (type->isPointerType()) {
240 callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
241 type->getPointeeType()->getAsCXXRecordDecl());
242 } else {
243 callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
244 type.getNonReferenceType()->getAsCXXRecordDecl());
245 }
246
247 // This wasn't a specialization? Huh?
248 if (!callback_decl) {
249 error_emitter_->EmitWarning(
250 loc,
251 "Skipping Callback* Identifier doesn't refer to class or template.");
252 return false;
253 }
254
255 // Expect only one argument in the runner. That argument will be a Tuple type.
256 const TemplateArgumentList& template_args = callback_decl->getTemplateArgs();
257 if (template_args.size() != 1) {
258 error_emitter_->EmitWarning(
259 loc,
260 "Skipping Callback*. "
261 "Expected to find TupleN<> in Runner specialization.");
262 return false;
263 }
264
265 CXXRecordDecl* runner_specialization =
266 template_args[0].getAsType()->getAsCXXRecordDecl();
267
268 if (!runner_specialization ||
269 runner_specialization->getNameAsString().find("Tuple") != 0) {
270 error_emitter_->EmitWarning(
271 loc,
272 "Skipping Callback*. "
273 "Expected to find TupleN<> in Runner specialization.");
274 return false;
275 }
276
277 ClassTemplateSpecializationDecl* tuple_decl =
278 dyn_cast<ClassTemplateSpecializationDecl>(runner_specialization);
279 if (!tuple_decl &&
280 runner_specialization->getNameAsString() != "Tuple0") {
281 error_emitter_->EmitWarning(loc, "Skipping Callback*. Expected Tuple0.");
282 return false;
283 }
284
285 if (!tuple_decl) {
286 *output += "base::Closure";
287 } else {
288 ConvertTupleType(tuple_decl->getTemplateArgs(), output);
289 }
290
291 return true;
292 }
293
294 bool OldCallbackTransform::CallbackFromTypeWithReturn(QualType type,
295 SourceLocation loc,
296 std::string* output) {
297 // We know we're a CallbackFromTypeWithReturn<>::Type value now so just dig
298 // out the info.
299 CXXRecordDecl* callback_decl;
300 if (type->isPointerType()) {
301 callback_decl = type->getPointeeType()->getAsCXXRecordDecl();
302 } else {
303 callback_decl = type.getNonReferenceType()->getAsCXXRecordDecl();
304 }
305
306 ClassTemplateSpecializationDecl* specialization_decl =
307 dyn_cast<ClassTemplateSpecializationDecl>(callback_decl->getParent());
308
309 string buffer;
310 llvm::raw_string_ostream output_os(buffer);
311 output_os << "base::Callback<";
312 specialization_decl->getTemplateArgs()[0].print(context_->PrintingPolicy,
313 output_os);
314 output_os << "(void)>";
315
316 output_os.flush();
317 output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
318
319 return true;
320 }
321
322 bool OldCallbackTransform::IsOldCallbackType(QualType type,
323 bool* is_callback_with_return) {
324
325 // First off, is this thing class, or a poitner to a class?
326 QualType bare_type = type.getNonReferenceType();
327 if (bare_type->isPointerType()) {
328 bare_type = bare_type->getPointeeType();
329 }
330 const CXXRecordDecl* type_decl = bare_type->getCXXRecordDeclForPointerType();
331 if (!type_decl) type_decl = bare_type->getAsCXXRecordDecl();
332 if (!type_decl) return false;
333 type_decl = type_decl->getCanonicalDecl();
334
335 // Yay. A class. How quiant. Okay "class," what's your name?
336 string name = type_decl->getNameAsString();
337
338 // A CallbackRunner huh? Well, aren't you special?
339 if (name == "CallbackRunner") return true;
340
341 // We don't normally accept Types, but when we do, we accept
342 // CallbackWithReturnValue::Types.
343 if (name == "Type") {
344 if (const CXXRecordDecl* encloding_class =
345 dyn_cast_or_null<CXXRecordDecl>(type_decl->getParent())) {
346 if (encloding_class->getNameAsString() == "CallbackWithReturnValue") {
347 *is_callback_with_return = true;
348 return true;
349 }
350 }
351 }
352
353 return false;
354 }
355
356 bool OldCallbackTransform::IsOldCallbackGenerator(Stmt* statement) {
357 CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
358 if (!ce) return false;
359
360 FunctionDecl *fd = ce->getDirectCallee();
361 if (!fd) return false;
362
363 std::string function_name = fd->getNameInfo().getName().getAsString();
364 if ("NewCallback" != function_name &&
365 "NewCallbackWithReturnValue" != function_name) {
366 return false;
367 }
368
369 if (ce->getNumArgs() < 2) {
370 error_emitter_->EmitWarning(
371 statement->getLocStart(),
372 "NewCallback.* with less than 2 args?! Inconceivable!");
373 return false;
374 }
375
376 return true;
377 }
378
379 bool OldCallbackTransform::IsOldCallbackRun(Stmt* statement) {
380 bool is_callback_with_return = false;
381 CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
382 if (!ce) return false;
383
384 CXXMethodDecl *md = dyn_cast_or_null<CXXMethodDecl>(ce->getDirectCallee());
385 if (!md) return false;
386
387 std::string function_name = md->getNameInfo().getName().getAsString();
388 if ("Run" != function_name) return false;
389
390 if (!IsOldCallbackType(
391 md->getParent()->getTypeForDecl()->getCanonicalTypeUnqualified(),
392 &is_callback_with_return)) {
393 return false;
394 }
395
396 return true;
397 }
398
399 void OldCallbackTransform::ConvertTupleType(
400 const TemplateArgumentList& template_args,
401 std::string* output) {
402 string buffer;
403 llvm::raw_string_ostream output_os(buffer);
404 output_os << "base::Callback<void(";
405 for (size_t i = 0; i < template_args.size(); ++i) {
406 template_args[i].print(context_->PrintingPolicy, output_os);
407 if (i != (template_args.size() - 1)) {
408 output_os << ", ";
409 }
410 }
411 output_os << ")>";
412
413 output_os.flush();
414 output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
415 }
416
417 } // namespace clang
OLDNEW
« no previous file with comments | « tools/clang/BindMigrate/OldCallbackTransform.h ('k') | tools/clang/BindMigrate/PostTaskTransform.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698