blob: feecc70ec1450370197f73502317429ffe41bce7 [file] [log] [blame]
Ted Kremenek30660a82012-03-06 20:06:33 +00001//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Rewrites legacy method calls to modern syntax.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Edit/Rewriters.h"
15#include "clang/Edit/Commit.h"
16#include "clang/Lex/Lexer.h"
17#include "clang/AST/ExprObjC.h"
18#include "clang/AST/ExprCXX.h"
19#include "clang/AST/NSAPI.h"
20
21using namespace clang;
22using namespace edit;
23
24static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000025 IdentifierInfo *&ClassId,
26 const LangOptions &LangOpts) {
Ted Kremenek30660a82012-03-06 20:06:33 +000027 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
28 return false;
29
30 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
31 if (!Receiver)
32 return false;
33 ClassId = Receiver->getIdentifier();
34
35 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
36 return true;
37
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000038 // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
39 // since the change from +1 to +0 will be handled fine by ARC.
40 if (LangOpts.ObjCAutoRefCount) {
41 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
42 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
43 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
44 if (Rec->getMethodFamily() == OMF_alloc)
45 return true;
46 }
47 }
48 }
49
Ted Kremenek30660a82012-03-06 20:06:33 +000050 return false;
51}
52
53//===----------------------------------------------------------------------===//
54// rewriteObjCRedundantCallWithLiteral.
55//===----------------------------------------------------------------------===//
56
57bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
58 const NSAPI &NS, Commit &commit) {
59 IdentifierInfo *II = 0;
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000060 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-03-06 20:06:33 +000061 return false;
62 if (Msg->getNumArgs() != 1)
63 return false;
64
65 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
66 Selector Sel = Msg->getSelector();
67
68 if ((isa<ObjCStringLiteral>(Arg) &&
69 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000070 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
71 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000072
73 (isa<ObjCArrayLiteral>(Arg) &&
74 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000075 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
76 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) ||
Ted Kremenek30660a82012-03-06 20:06:33 +000077
78 (isa<ObjCDictionaryLiteral>(Arg) &&
79 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +000080 (NS.getNSDictionarySelector(
81 NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
82 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
Ted Kremenek30660a82012-03-06 20:06:33 +000083
84 commit.replaceWithInner(Msg->getSourceRange(),
85 Msg->getArg(0)->getSourceRange());
86 return true;
87 }
88
89 return false;
90}
91
92//===----------------------------------------------------------------------===//
93// rewriteToObjCSubscriptSyntax.
94//===----------------------------------------------------------------------===//
95
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +000096static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
97
Ted Kremenek30660a82012-03-06 20:06:33 +000098static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +000099 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000100 SourceRange RecRange = Receiver->getSourceRange();
101 commit.insertWrap("(", RecRange, ")");
102 }
103}
104
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000105static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
106 Commit &commit) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000107 if (Msg->getNumArgs() != 1)
108 return false;
109 const Expr *Rec = Msg->getInstanceReceiver();
110 if (!Rec)
111 return false;
112
113 SourceRange MsgRange = Msg->getSourceRange();
114 SourceRange RecRange = Rec->getSourceRange();
115 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
116
117 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
118 ArgRange.getBegin()),
119 CharSourceRange::getTokenRange(RecRange));
120 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
121 ArgRange);
122 commit.insertWrap("[", ArgRange, "]");
123 maybePutParensOnReceiver(Rec, commit);
124 return true;
125}
126
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000127static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
128 const ObjCMessageExpr *Msg,
129 const NSAPI &NS,
130 Commit &commit) {
Argyrios Kyrtzidis12b732a2012-06-19 02:22:02 +0000131 if (!IFace->lookupInstanceMethod(NS.getObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000132 return false;
133 return rewriteToSubscriptGetCommon(Msg, commit);
134}
135
136static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
137 const ObjCMessageExpr *Msg,
138 const NSAPI &NS,
139 Commit &commit) {
Argyrios Kyrtzidis12b732a2012-06-19 02:22:02 +0000140 if (!IFace->lookupInstanceMethod(NS.getObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000141 return false;
142 return rewriteToSubscriptGetCommon(Msg, commit);
143}
144
145static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
146 const ObjCMessageExpr *Msg,
147 const NSAPI &NS,
Ted Kremenek30660a82012-03-06 20:06:33 +0000148 Commit &commit) {
149 if (Msg->getNumArgs() != 2)
150 return false;
151 const Expr *Rec = Msg->getInstanceReceiver();
152 if (!Rec)
153 return false;
Argyrios Kyrtzidis12b732a2012-06-19 02:22:02 +0000154 if (!IFace->lookupInstanceMethod(NS.getSetObjectAtIndexedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000155 return false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000156
157 SourceRange MsgRange = Msg->getSourceRange();
158 SourceRange RecRange = Rec->getSourceRange();
159 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
160 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
161
162 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
163 Arg0Range.getBegin()),
164 CharSourceRange::getTokenRange(RecRange));
165 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
166 Arg1Range.getBegin()),
167 CharSourceRange::getTokenRange(Arg0Range));
168 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
169 Arg1Range);
170 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
171 Arg1Range.getBegin()),
172 "] = ");
173 maybePutParensOnReceiver(Rec, commit);
174 return true;
175}
176
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000177static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
178 const ObjCMessageExpr *Msg,
179 const NSAPI &NS,
Ted Kremenek30660a82012-03-06 20:06:33 +0000180 Commit &commit) {
181 if (Msg->getNumArgs() != 2)
182 return false;
183 const Expr *Rec = Msg->getInstanceReceiver();
184 if (!Rec)
185 return false;
Argyrios Kyrtzidis12b732a2012-06-19 02:22:02 +0000186 if (!IFace->lookupInstanceMethod(NS.getSetObjectForKeyedSubscriptSelector()))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000187 return false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000188
189 SourceRange MsgRange = Msg->getSourceRange();
190 SourceRange RecRange = Rec->getSourceRange();
191 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
192 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
193
194 SourceLocation LocBeforeVal = Arg0Range.getBegin();
195 commit.insertBefore(LocBeforeVal, "] = ");
196 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
197 /*beforePreviousInsertions=*/true);
198 commit.insertBefore(LocBeforeVal, "[");
199 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
200 Arg0Range.getBegin()),
201 CharSourceRange::getTokenRange(RecRange));
202 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
203 Arg0Range);
204 maybePutParensOnReceiver(Rec, commit);
205 return true;
206}
207
208bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000209 const NSAPI &NS, Commit &commit) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000210 if (!Msg || Msg->isImplicit() ||
211 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
212 return false;
213 const ObjCMethodDecl *Method = Msg->getMethodDecl();
214 if (!Method)
215 return false;
216
217 const ObjCInterfaceDecl *
218 IFace = NS.getASTContext().getObjContainingInterface(
219 const_cast<ObjCMethodDecl *>(Method));
220 if (!IFace)
221 return false;
222 IdentifierInfo *II = IFace->getIdentifier();
223 Selector Sel = Msg->getSelector();
224
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000225 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
226 Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
227 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
228
229 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
230 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
231 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000232
233 if (Msg->getNumArgs() != 2)
234 return false;
235
236 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
237 Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000238 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000239
240 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
241 Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
Argyrios Kyrtzidiscacf7182012-06-04 21:23:26 +0000242 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000243
244 return false;
245}
246
247//===----------------------------------------------------------------------===//
248// rewriteToObjCLiteralSyntax.
249//===----------------------------------------------------------------------===//
250
251static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
252 const NSAPI &NS, Commit &commit);
253static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
254 const NSAPI &NS, Commit &commit);
255static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
256 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000257static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
258 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000259static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
260 const NSAPI &NS, Commit &commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000261
262bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
263 const NSAPI &NS, Commit &commit) {
264 IdentifierInfo *II = 0;
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000265 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
Ted Kremenek30660a82012-03-06 20:06:33 +0000266 return false;
267
268 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
269 return rewriteToArrayLiteral(Msg, NS, commit);
270 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
271 return rewriteToDictionaryLiteral(Msg, NS, commit);
272 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
273 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000274 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
275 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000276
277 return false;
278}
279
280//===----------------------------------------------------------------------===//
281// rewriteToArrayLiteral.
282//===----------------------------------------------------------------------===//
283
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000284/// \brief Adds an explicit cast to 'id' if the type is not objc object.
285static void objectifyExpr(const Expr *E, Commit &commit);
286
Ted Kremenek30660a82012-03-06 20:06:33 +0000287static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
288 const NSAPI &NS, Commit &commit) {
289 Selector Sel = Msg->getSelector();
290 SourceRange MsgRange = Msg->getSourceRange();
291
292 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
293 if (Msg->getNumArgs() != 0)
294 return false;
295 commit.replace(MsgRange, "@[]");
296 return true;
297 }
298
299 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
300 if (Msg->getNumArgs() != 1)
301 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000302 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000303 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
304 commit.replaceWithInner(MsgRange, ArgRange);
305 commit.insertWrap("@[", ArgRange, "]");
306 return true;
307 }
308
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000309 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
310 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000311 if (Msg->getNumArgs() == 0)
312 return false;
313 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
314 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
315 return false;
316
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000317 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
318 objectifyExpr(Msg->getArg(i), commit);
319
Ted Kremenek30660a82012-03-06 20:06:33 +0000320 if (Msg->getNumArgs() == 1) {
321 commit.replace(MsgRange, "@[]");
322 return true;
323 }
324 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
325 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
326 commit.replaceWithInner(MsgRange, ArgRange);
327 commit.insertWrap("@[", ArgRange, "]");
328 return true;
329 }
330
331 return false;
332}
333
334//===----------------------------------------------------------------------===//
335// rewriteToDictionaryLiteral.
336//===----------------------------------------------------------------------===//
337
338static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
339 const NSAPI &NS, Commit &commit) {
340 Selector Sel = Msg->getSelector();
341 SourceRange MsgRange = Msg->getSourceRange();
342
343 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
344 if (Msg->getNumArgs() != 0)
345 return false;
346 commit.replace(MsgRange, "@{}");
347 return true;
348 }
349
350 if (Sel == NS.getNSDictionarySelector(
351 NSAPI::NSDict_dictionaryWithObjectForKey)) {
352 if (Msg->getNumArgs() != 2)
353 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000354
355 objectifyExpr(Msg->getArg(0), commit);
356 objectifyExpr(Msg->getArg(1), commit);
357
Ted Kremenek30660a82012-03-06 20:06:33 +0000358 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
359 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
360 // Insert key before the value.
361 commit.insertBefore(ValRange.getBegin(), ": ");
362 commit.insertFromRange(ValRange.getBegin(),
363 CharSourceRange::getTokenRange(KeyRange),
364 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
365 commit.insertBefore(ValRange.getBegin(), "@{");
366 commit.insertAfterToken(ValRange.getEnd(), "}");
367 commit.replaceWithInner(MsgRange, ValRange);
368 return true;
369 }
370
371 if (Sel == NS.getNSDictionarySelector(
Argyrios Kyrtzidis6b4db9b2012-06-06 22:23:12 +0000372 NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
373 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
Ted Kremenek30660a82012-03-06 20:06:33 +0000374 if (Msg->getNumArgs() % 2 != 1)
375 return false;
376 unsigned SentinelIdx = Msg->getNumArgs() - 1;
377 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
378 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
379 return false;
380
381 if (Msg->getNumArgs() == 1) {
382 commit.replace(MsgRange, "@{}");
383 return true;
384 }
385
386 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000387 objectifyExpr(Msg->getArg(i), commit);
388 objectifyExpr(Msg->getArg(i+1), commit);
389
Ted Kremenek30660a82012-03-06 20:06:33 +0000390 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
391 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
392 // Insert value after key.
393 commit.insertAfterToken(KeyRange.getEnd(), ": ");
394 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
395 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
396 KeyRange.getBegin()));
397 }
398 // Range of arguments up until and including the last key.
399 // The sentinel and first value are cut off, the value will move after the
400 // key.
401 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
402 Msg->getArg(SentinelIdx-1)->getLocEnd());
403 commit.insertWrap("@{", ArgRange, "}");
404 commit.replaceWithInner(MsgRange, ArgRange);
405 return true;
406 }
407
408 return false;
409}
410
411//===----------------------------------------------------------------------===//
412// rewriteToNumberLiteral.
413//===----------------------------------------------------------------------===//
414
415static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
416 const CharacterLiteral *Arg,
417 const NSAPI &NS, Commit &commit) {
418 if (Arg->getKind() != CharacterLiteral::Ascii)
419 return false;
420 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
421 Msg->getSelector())) {
422 SourceRange ArgRange = Arg->getSourceRange();
423 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
424 commit.insert(ArgRange.getBegin(), "@");
425 return true;
426 }
427
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000428 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000429}
430
431static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
432 const Expr *Arg,
433 const NSAPI &NS, Commit &commit) {
434 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
435 Msg->getSelector())) {
436 SourceRange ArgRange = Arg->getSourceRange();
437 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
438 commit.insert(ArgRange.getBegin(), "@");
439 return true;
440 }
441
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000442 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000443}
444
445namespace {
446
447struct LiteralInfo {
448 bool Hex, Octal;
449 StringRef U, F, L, LL;
450 CharSourceRange WithoutSuffRange;
451};
452
453}
454
455static bool getLiteralInfo(SourceRange literalRange,
456 bool isFloat, bool isIntZero,
457 ASTContext &Ctx, LiteralInfo &Info) {
458 if (literalRange.getBegin().isMacroID() ||
459 literalRange.getEnd().isMacroID())
460 return false;
461 StringRef text = Lexer::getSourceText(
462 CharSourceRange::getTokenRange(literalRange),
David Blaikie4e4d0842012-03-11 07:00:24 +0000463 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000464 if (text.empty())
465 return false;
466
467 llvm::Optional<bool> UpperU, UpperL;
468 bool UpperF = false;
469
470 struct Suff {
471 static bool has(StringRef suff, StringRef &text) {
472 if (text.endswith(suff)) {
473 text = text.substr(0, text.size()-suff.size());
474 return true;
475 }
476 return false;
477 }
478 };
479
480 while (1) {
481 if (Suff::has("u", text)) {
482 UpperU = false;
483 } else if (Suff::has("U", text)) {
484 UpperU = true;
485 } else if (Suff::has("ll", text)) {
486 UpperL = false;
487 } else if (Suff::has("LL", text)) {
488 UpperL = true;
489 } else if (Suff::has("l", text)) {
490 UpperL = false;
491 } else if (Suff::has("L", text)) {
492 UpperL = true;
493 } else if (isFloat && Suff::has("f", text)) {
494 UpperF = false;
495 } else if (isFloat && Suff::has("F", text)) {
496 UpperF = true;
497 } else
498 break;
499 }
500
501 if (!UpperU.hasValue() && !UpperL.hasValue())
502 UpperU = UpperL = true;
503 else if (UpperU.hasValue() && !UpperL.hasValue())
504 UpperL = UpperU;
505 else if (UpperL.hasValue() && !UpperU.hasValue())
506 UpperU = UpperL;
507
508 Info.U = *UpperU ? "U" : "u";
509 Info.L = *UpperL ? "L" : "l";
510 Info.LL = *UpperL ? "LL" : "ll";
511 Info.F = UpperF ? "F" : "f";
512
513 Info.Hex = Info.Octal = false;
514 if (text.startswith("0x"))
515 Info.Hex = true;
516 else if (!isFloat && !isIntZero && text.startswith("0"))
517 Info.Octal = true;
518
519 SourceLocation B = literalRange.getBegin();
520 Info.WithoutSuffRange =
521 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
522 return true;
523}
524
525static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
526 const NSAPI &NS, Commit &commit) {
527 if (Msg->getNumArgs() != 1)
528 return false;
529
530 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
531 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
532 return rewriteToCharLiteral(Msg, CharE, NS, commit);
533 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
534 return rewriteToBoolLiteral(Msg, BE, NS, commit);
535 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
536 return rewriteToBoolLiteral(Msg, BE, NS, commit);
537
538 const Expr *literalE = Arg;
539 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
540 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
541 literalE = UOE->getSubExpr();
542 }
543
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000544 // Only integer and floating literals, otherwise try to rewrite to boxed
545 // expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000546 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000547 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000548
549 ASTContext &Ctx = NS.getASTContext();
550 Selector Sel = Msg->getSelector();
551 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
552 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
553 if (!MKOpt)
554 return false;
555 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
556
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000557 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000558 bool CallIsFloating = false, CallIsDouble = false;
559
560 switch (MK) {
561 // We cannot have these calls with int/float literals.
562 case NSAPI::NSNumberWithChar:
563 case NSAPI::NSNumberWithUnsignedChar:
564 case NSAPI::NSNumberWithShort:
565 case NSAPI::NSNumberWithUnsignedShort:
566 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000567 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000568
569 case NSAPI::NSNumberWithUnsignedInt:
570 case NSAPI::NSNumberWithUnsignedInteger:
571 CallIsUnsigned = true;
572 case NSAPI::NSNumberWithInt:
573 case NSAPI::NSNumberWithInteger:
Ted Kremenek30660a82012-03-06 20:06:33 +0000574 break;
575
576 case NSAPI::NSNumberWithUnsignedLong:
577 CallIsUnsigned = true;
578 case NSAPI::NSNumberWithLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000579 CallIsLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000580 break;
581
582 case NSAPI::NSNumberWithUnsignedLongLong:
583 CallIsUnsigned = true;
584 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000585 CallIsLongLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000586 break;
587
588 case NSAPI::NSNumberWithDouble:
589 CallIsDouble = true;
590 case NSAPI::NSNumberWithFloat:
591 CallIsFloating = true;
592 break;
593 }
594
595 SourceRange ArgRange = Arg->getSourceRange();
596 QualType ArgTy = Arg->getType();
597 QualType CallTy = Msg->getArg(0)->getType();
598
599 // Check for the easy case, the literal maps directly to the call.
600 if (Ctx.hasSameType(ArgTy, CallTy)) {
601 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
602 commit.insert(ArgRange.getBegin(), "@");
603 return true;
604 }
605
606 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000607 // Try with boxed expression if it came from a macro.
Ted Kremenek30660a82012-03-06 20:06:33 +0000608 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000609 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000610
611 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000612 // For a float passed to integer call, don't try rewriting to objc literal.
613 // It is difficult and a very uncommon case anyway.
614 // But try with boxed expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000615 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000616 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000617
618 // Try to modify the literal make it the same type as the method call.
619 // -Modify the suffix, and/or
620 // -Change integer to float
621
622 LiteralInfo LitInfo;
623 bool isIntZero = false;
624 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
625 isIntZero = !IntE->getValue().getBoolValue();
626 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000627 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000628
629 // Not easy to do int -> float with hex/octal and uncommon anyway.
630 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000631 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000632
633 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
634 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
635
636 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
637 LitInfo.WithoutSuffRange);
638 commit.insert(LitB, "@");
639
640 if (!LitIsFloat && CallIsFloating)
641 commit.insert(LitE, ".0");
642
643 if (CallIsFloating) {
644 if (!CallIsDouble)
645 commit.insert(LitE, LitInfo.F);
646 } else {
647 if (CallIsUnsigned)
648 commit.insert(LitE, LitInfo.U);
649
650 if (CallIsLong)
651 commit.insert(LitE, LitInfo.L);
652 else if (CallIsLongLong)
653 commit.insert(LitE, LitInfo.LL);
654 }
655 return true;
656}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000657
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000658// FIXME: Make determination of operator precedence more general and
659// make it broadly available.
660static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
661 const Expr* Expr = FullExpr->IgnoreImpCasts();
662 if (isa<ArraySubscriptExpr>(Expr) ||
663 isa<CallExpr>(Expr) ||
664 isa<DeclRefExpr>(Expr) ||
665 isa<CXXNamedCastExpr>(Expr) ||
666 isa<CXXConstructExpr>(Expr) ||
667 isa<CXXThisExpr>(Expr) ||
668 isa<CXXTypeidExpr>(Expr) ||
669 isa<CXXUnresolvedConstructExpr>(Expr) ||
670 isa<ObjCMessageExpr>(Expr) ||
671 isa<ObjCPropertyRefExpr>(Expr) ||
672 isa<ObjCProtocolExpr>(Expr) ||
673 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000674 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000675 isa<ParenExpr>(FullExpr) ||
676 isa<ParenListExpr>(Expr) ||
677 isa<SizeOfPackExpr>(Expr))
678 return false;
679
680 return true;
681}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000682static bool castOperatorNeedsParens(const Expr *FullExpr) {
683 const Expr* Expr = FullExpr->IgnoreImpCasts();
684 if (isa<ArraySubscriptExpr>(Expr) ||
685 isa<CallExpr>(Expr) ||
686 isa<DeclRefExpr>(Expr) ||
687 isa<CastExpr>(Expr) ||
688 isa<CXXNewExpr>(Expr) ||
689 isa<CXXConstructExpr>(Expr) ||
690 isa<CXXDeleteExpr>(Expr) ||
691 isa<CXXNoexceptExpr>(Expr) ||
692 isa<CXXPseudoDestructorExpr>(Expr) ||
693 isa<CXXScalarValueInitExpr>(Expr) ||
694 isa<CXXThisExpr>(Expr) ||
695 isa<CXXTypeidExpr>(Expr) ||
696 isa<CXXUnresolvedConstructExpr>(Expr) ||
697 isa<ObjCMessageExpr>(Expr) ||
698 isa<ObjCPropertyRefExpr>(Expr) ||
699 isa<ObjCProtocolExpr>(Expr) ||
700 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000701 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000702 isa<ParenExpr>(FullExpr) ||
703 isa<ParenListExpr>(Expr) ||
704 isa<SizeOfPackExpr>(Expr) ||
705 isa<UnaryOperator>(Expr))
706 return false;
707
708 return true;
709}
710
711static void objectifyExpr(const Expr *E, Commit &commit) {
712 if (!E) return;
713
714 QualType T = E->getType();
715 if (T->isObjCObjectPointerType()) {
716 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
717 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
718 return;
719 } else {
720 return;
721 }
722 } else if (!T->isPointerType()) {
723 return;
724 }
725
726 SourceRange Range = E->getSourceRange();
727 if (castOperatorNeedsParens(E))
728 commit.insertWrap("(", Range, ")");
729 commit.insertBefore(Range.getBegin(), "(id)");
730}
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000731
732//===----------------------------------------------------------------------===//
733// rewriteToNumericBoxedExpression.
734//===----------------------------------------------------------------------===//
735
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000736static bool isEnumConstant(const Expr *E) {
737 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
738 if (const ValueDecl *VD = DRE->getDecl())
739 return isa<EnumConstantDecl>(VD);
740
741 return false;
742}
743
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000744static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
745 const NSAPI &NS, Commit &commit) {
746 if (Msg->getNumArgs() != 1)
747 return false;
748
749 const Expr *Arg = Msg->getArg(0);
750 if (Arg->isTypeDependent())
751 return false;
752
753 ASTContext &Ctx = NS.getASTContext();
754 Selector Sel = Msg->getSelector();
755 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
756 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
757 if (!MKOpt)
758 return false;
759 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
760
761 const Expr *OrigArg = Arg->IgnoreImpCasts();
762 QualType FinalTy = Arg->getType();
763 QualType OrigTy = OrigArg->getType();
764 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
765 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
766
767 bool isTruncated = FinalTySize < OrigTySize;
768 bool needsCast = false;
769
770 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
771 switch (ICE->getCastKind()) {
772 case CK_LValueToRValue:
773 case CK_NoOp:
774 case CK_UserDefinedConversion:
775 break;
776
777 case CK_IntegralCast: {
778 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
779 break;
780 // Be more liberal with Integer/UnsignedInteger which are very commonly
781 // used.
782 if ((MK == NSAPI::NSNumberWithInteger ||
783 MK == NSAPI::NSNumberWithUnsignedInteger) &&
784 !isTruncated) {
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000785 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000786 break;
787 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
788 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
789 break;
790 }
791
792 needsCast = true;
793 break;
794 }
795
796 case CK_PointerToBoolean:
797 case CK_IntegralToBoolean:
798 case CK_IntegralToFloating:
799 case CK_FloatingToIntegral:
800 case CK_FloatingToBoolean:
801 case CK_FloatingCast:
802 case CK_FloatingComplexToReal:
803 case CK_FloatingComplexToBoolean:
804 case CK_IntegralComplexToReal:
805 case CK_IntegralComplexToBoolean:
806 case CK_AtomicToNonAtomic:
807 needsCast = true;
808 break;
809
810 case CK_Dependent:
811 case CK_BitCast:
812 case CK_LValueBitCast:
813 case CK_BaseToDerived:
814 case CK_DerivedToBase:
815 case CK_UncheckedDerivedToBase:
816 case CK_Dynamic:
817 case CK_ToUnion:
818 case CK_ArrayToPointerDecay:
819 case CK_FunctionToPointerDecay:
820 case CK_NullToPointer:
821 case CK_NullToMemberPointer:
822 case CK_BaseToDerivedMemberPointer:
823 case CK_DerivedToBaseMemberPointer:
824 case CK_MemberPointerToBoolean:
825 case CK_ReinterpretMemberPointer:
826 case CK_ConstructorConversion:
827 case CK_IntegralToPointer:
828 case CK_PointerToIntegral:
829 case CK_ToVoid:
830 case CK_VectorSplat:
831 case CK_CPointerToObjCPointerCast:
832 case CK_BlockPointerToObjCPointerCast:
833 case CK_AnyPointerToBlockPointerCast:
834 case CK_ObjCObjectLValueCast:
835 case CK_FloatingRealToComplex:
836 case CK_FloatingComplexCast:
837 case CK_FloatingComplexToIntegralComplex:
838 case CK_IntegralRealToComplex:
839 case CK_IntegralComplexCast:
840 case CK_IntegralComplexToFloatingComplex:
841 case CK_ARCProduceObject:
842 case CK_ARCConsumeObject:
843 case CK_ARCReclaimReturnedObject:
844 case CK_ARCExtendBlockObject:
845 case CK_NonAtomicToAtomic:
846 case CK_CopyAndAutoreleaseBlockObject:
847 return false;
848 }
849 }
850
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +0000851 if (needsCast) {
852 DiagnosticsEngine &Diags = Ctx.getDiagnostics();
853 // FIXME: Use a custom category name to distinguish migration diagnostics.
854 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
855 "converting to boxing syntax requires a cast");
856 Diags.Report(Msg->getExprLoc(), diagID) << Msg->getSourceRange();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000857 return false;
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +0000858 }
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000859
860 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000861 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000862
863 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
864 commit.insertBefore(ArgRange.getBegin(), "@");
865 else
866 commit.insertWrap("@(", ArgRange, ")");
867
868 return true;
869}
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000870
871//===----------------------------------------------------------------------===//
872// rewriteToStringBoxedExpression.
873//===----------------------------------------------------------------------===//
874
875static bool doRewriteToUTF8StringBoxedExpressionHelper(
876 const ObjCMessageExpr *Msg,
877 const NSAPI &NS, Commit &commit) {
878 const Expr *Arg = Msg->getArg(0);
879 if (Arg->isTypeDependent())
880 return false;
881
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000882 ASTContext &Ctx = NS.getASTContext();
883
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000884 const Expr *OrigArg = Arg->IgnoreImpCasts();
885 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000886 if (OrigTy->isArrayType())
887 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000888
889 if (const StringLiteral *
890 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
891 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
892 commit.insert(StrE->getLocStart(), "@");
893 return true;
894 }
895
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000896 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
897 QualType PointeeType = PT->getPointeeType();
898 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
899 SourceRange ArgRange = OrigArg->getSourceRange();
900 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
901
902 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
903 commit.insertBefore(ArgRange.getBegin(), "@");
904 else
905 commit.insertWrap("@(", ArgRange, ")");
906
907 return true;
908 }
909 }
910
911 return false;
912}
913
914static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
915 const NSAPI &NS, Commit &commit) {
916 Selector Sel = Msg->getSelector();
917
918 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
919 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
920 if (Msg->getNumArgs() != 1)
921 return false;
922 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
923 }
924
925 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
926 if (Msg->getNumArgs() != 2)
927 return false;
928
929 const Expr *encodingArg = Msg->getArg(1);
930 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
931 NS.isNSASCIIStringEncodingConstant(encodingArg))
932 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
933 }
934
935 return false;
936}