blob: 1d368d6bcd470d56c20d0b59318d6e3780b3cc74 [file] [log] [blame]
Ted Kremenekf7639e12012-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,
25 IdentifierInfo *&ClassId) {
26 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
27 return false;
28
29 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
30 if (!Receiver)
31 return false;
32 ClassId = Receiver->getIdentifier();
33
34 if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
35 return true;
36
37 return false;
38}
39
40//===----------------------------------------------------------------------===//
41// rewriteObjCRedundantCallWithLiteral.
42//===----------------------------------------------------------------------===//
43
44bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
45 const NSAPI &NS, Commit &commit) {
46 IdentifierInfo *II = 0;
47 if (!checkForLiteralCreation(Msg, II))
48 return false;
49 if (Msg->getNumArgs() != 1)
50 return false;
51
52 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
53 Selector Sel = Msg->getSelector();
54
55 if ((isa<ObjCStringLiteral>(Arg) &&
56 NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
57 NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) ||
58
59 (isa<ObjCArrayLiteral>(Arg) &&
60 NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
61 NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) ||
62
63 (isa<ObjCDictionaryLiteral>(Arg) &&
64 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
65 NS.getNSDictionarySelector(
66 NSAPI::NSDict_dictionaryWithDictionary) == Sel)) {
67
68 commit.replaceWithInner(Msg->getSourceRange(),
69 Msg->getArg(0)->getSourceRange());
70 return true;
71 }
72
73 return false;
74}
75
76//===----------------------------------------------------------------------===//
77// rewriteToObjCSubscriptSyntax.
78//===----------------------------------------------------------------------===//
79
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +000080static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
81
Ted Kremenekf7639e12012-03-06 20:06:33 +000082static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +000083 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenekf7639e12012-03-06 20:06:33 +000084 SourceRange RecRange = Receiver->getSourceRange();
85 commit.insertWrap("(", RecRange, ")");
86 }
87}
88
89static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) {
90 if (Msg->getNumArgs() != 1)
91 return false;
92 const Expr *Rec = Msg->getInstanceReceiver();
93 if (!Rec)
94 return false;
95
96 SourceRange MsgRange = Msg->getSourceRange();
97 SourceRange RecRange = Rec->getSourceRange();
98 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
99
100 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
101 ArgRange.getBegin()),
102 CharSourceRange::getTokenRange(RecRange));
103 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
104 ArgRange);
105 commit.insertWrap("[", ArgRange, "]");
106 maybePutParensOnReceiver(Rec, commit);
107 return true;
108}
109
110static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg,
111 Commit &commit) {
112 if (Msg->getNumArgs() != 2)
113 return false;
114 const Expr *Rec = Msg->getInstanceReceiver();
115 if (!Rec)
116 return false;
117
118 SourceRange MsgRange = Msg->getSourceRange();
119 SourceRange RecRange = Rec->getSourceRange();
120 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
121 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
122
123 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
124 Arg0Range.getBegin()),
125 CharSourceRange::getTokenRange(RecRange));
126 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
127 Arg1Range.getBegin()),
128 CharSourceRange::getTokenRange(Arg0Range));
129 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
130 Arg1Range);
131 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
132 Arg1Range.getBegin()),
133 "] = ");
134 maybePutParensOnReceiver(Rec, commit);
135 return true;
136}
137
138static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg,
139 Commit &commit) {
140 if (Msg->getNumArgs() != 2)
141 return false;
142 const Expr *Rec = Msg->getInstanceReceiver();
143 if (!Rec)
144 return false;
145
146 SourceRange MsgRange = Msg->getSourceRange();
147 SourceRange RecRange = Rec->getSourceRange();
148 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
149 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
150
151 SourceLocation LocBeforeVal = Arg0Range.getBegin();
152 commit.insertBefore(LocBeforeVal, "] = ");
153 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
154 /*beforePreviousInsertions=*/true);
155 commit.insertBefore(LocBeforeVal, "[");
156 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
157 Arg0Range.getBegin()),
158 CharSourceRange::getTokenRange(RecRange));
159 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
160 Arg0Range);
161 maybePutParensOnReceiver(Rec, commit);
162 return true;
163}
164
165bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
166 const NSAPI &NS, Commit &commit) {
167 if (!Msg || Msg->isImplicit() ||
168 Msg->getReceiverKind() != ObjCMessageExpr::Instance)
169 return false;
170 const ObjCMethodDecl *Method = Msg->getMethodDecl();
171 if (!Method)
172 return false;
173
174 const ObjCInterfaceDecl *
175 IFace = NS.getASTContext().getObjContainingInterface(
176 const_cast<ObjCMethodDecl *>(Method));
177 if (!IFace)
178 return false;
179 IdentifierInfo *II = IFace->getIdentifier();
180 Selector Sel = Msg->getSelector();
181
182 if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
183 Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) ||
184 (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
185 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)))
186 return rewriteToSubscriptGet(Msg, commit);
187
188 if (Msg->getNumArgs() != 2)
189 return false;
190
191 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
192 Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
193 return rewriteToArraySubscriptSet(Msg, commit);
194
195 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
196 Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
197 return rewriteToDictionarySubscriptSet(Msg, commit);
198
199 return false;
200}
201
202//===----------------------------------------------------------------------===//
203// rewriteToObjCLiteralSyntax.
204//===----------------------------------------------------------------------===//
205
206static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
207 const NSAPI &NS, Commit &commit);
208static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
209 const NSAPI &NS, Commit &commit);
210static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
211 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000212static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
213 const NSAPI &NS, Commit &commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000214
215bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
216 const NSAPI &NS, Commit &commit) {
217 IdentifierInfo *II = 0;
218 if (!checkForLiteralCreation(Msg, II))
219 return false;
220
221 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
222 return rewriteToArrayLiteral(Msg, NS, commit);
223 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
224 return rewriteToDictionaryLiteral(Msg, NS, commit);
225 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
226 return rewriteToNumberLiteral(Msg, NS, commit);
227
228 return false;
229}
230
231//===----------------------------------------------------------------------===//
232// rewriteToArrayLiteral.
233//===----------------------------------------------------------------------===//
234
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000235/// \brief Adds an explicit cast to 'id' if the type is not objc object.
236static void objectifyExpr(const Expr *E, Commit &commit);
237
Ted Kremenekf7639e12012-03-06 20:06:33 +0000238static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
239 const NSAPI &NS, Commit &commit) {
240 Selector Sel = Msg->getSelector();
241 SourceRange MsgRange = Msg->getSourceRange();
242
243 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
244 if (Msg->getNumArgs() != 0)
245 return false;
246 commit.replace(MsgRange, "@[]");
247 return true;
248 }
249
250 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
251 if (Msg->getNumArgs() != 1)
252 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000253 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000254 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
255 commit.replaceWithInner(MsgRange, ArgRange);
256 commit.insertWrap("@[", ArgRange, "]");
257 return true;
258 }
259
260 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) {
261 if (Msg->getNumArgs() == 0)
262 return false;
263 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
264 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
265 return false;
266
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000267 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
268 objectifyExpr(Msg->getArg(i), commit);
269
Ted Kremenekf7639e12012-03-06 20:06:33 +0000270 if (Msg->getNumArgs() == 1) {
271 commit.replace(MsgRange, "@[]");
272 return true;
273 }
274 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
275 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
276 commit.replaceWithInner(MsgRange, ArgRange);
277 commit.insertWrap("@[", ArgRange, "]");
278 return true;
279 }
280
281 return false;
282}
283
284//===----------------------------------------------------------------------===//
285// rewriteToDictionaryLiteral.
286//===----------------------------------------------------------------------===//
287
288static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
289 const NSAPI &NS, Commit &commit) {
290 Selector Sel = Msg->getSelector();
291 SourceRange MsgRange = Msg->getSourceRange();
292
293 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
294 if (Msg->getNumArgs() != 0)
295 return false;
296 commit.replace(MsgRange, "@{}");
297 return true;
298 }
299
300 if (Sel == NS.getNSDictionarySelector(
301 NSAPI::NSDict_dictionaryWithObjectForKey)) {
302 if (Msg->getNumArgs() != 2)
303 return false;
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000304
305 objectifyExpr(Msg->getArg(0), commit);
306 objectifyExpr(Msg->getArg(1), commit);
307
Ted Kremenekf7639e12012-03-06 20:06:33 +0000308 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
309 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
310 // Insert key before the value.
311 commit.insertBefore(ValRange.getBegin(), ": ");
312 commit.insertFromRange(ValRange.getBegin(),
313 CharSourceRange::getTokenRange(KeyRange),
314 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
315 commit.insertBefore(ValRange.getBegin(), "@{");
316 commit.insertAfterToken(ValRange.getEnd(), "}");
317 commit.replaceWithInner(MsgRange, ValRange);
318 return true;
319 }
320
321 if (Sel == NS.getNSDictionarySelector(
322 NSAPI::NSDict_dictionaryWithObjectsAndKeys)) {
323 if (Msg->getNumArgs() % 2 != 1)
324 return false;
325 unsigned SentinelIdx = Msg->getNumArgs() - 1;
326 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
327 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
328 return false;
329
330 if (Msg->getNumArgs() == 1) {
331 commit.replace(MsgRange, "@{}");
332 return true;
333 }
334
335 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000336 objectifyExpr(Msg->getArg(i), commit);
337 objectifyExpr(Msg->getArg(i+1), commit);
338
Ted Kremenekf7639e12012-03-06 20:06:33 +0000339 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
340 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
341 // Insert value after key.
342 commit.insertAfterToken(KeyRange.getEnd(), ": ");
343 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
344 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
345 KeyRange.getBegin()));
346 }
347 // Range of arguments up until and including the last key.
348 // The sentinel and first value are cut off, the value will move after the
349 // key.
350 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
351 Msg->getArg(SentinelIdx-1)->getLocEnd());
352 commit.insertWrap("@{", ArgRange, "}");
353 commit.replaceWithInner(MsgRange, ArgRange);
354 return true;
355 }
356
357 return false;
358}
359
360//===----------------------------------------------------------------------===//
361// rewriteToNumberLiteral.
362//===----------------------------------------------------------------------===//
363
364static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
365 const CharacterLiteral *Arg,
366 const NSAPI &NS, Commit &commit) {
367 if (Arg->getKind() != CharacterLiteral::Ascii)
368 return false;
369 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
370 Msg->getSelector())) {
371 SourceRange ArgRange = Arg->getSourceRange();
372 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
373 commit.insert(ArgRange.getBegin(), "@");
374 return true;
375 }
376
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000377 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000378}
379
380static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
381 const Expr *Arg,
382 const NSAPI &NS, Commit &commit) {
383 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
384 Msg->getSelector())) {
385 SourceRange ArgRange = Arg->getSourceRange();
386 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
387 commit.insert(ArgRange.getBegin(), "@");
388 return true;
389 }
390
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000391 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000392}
393
394namespace {
395
396struct LiteralInfo {
397 bool Hex, Octal;
398 StringRef U, F, L, LL;
399 CharSourceRange WithoutSuffRange;
400};
401
402}
403
404static bool getLiteralInfo(SourceRange literalRange,
405 bool isFloat, bool isIntZero,
406 ASTContext &Ctx, LiteralInfo &Info) {
407 if (literalRange.getBegin().isMacroID() ||
408 literalRange.getEnd().isMacroID())
409 return false;
410 StringRef text = Lexer::getSourceText(
411 CharSourceRange::getTokenRange(literalRange),
David Blaikiebbafb8a2012-03-11 07:00:24 +0000412 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000413 if (text.empty())
414 return false;
415
416 llvm::Optional<bool> UpperU, UpperL;
417 bool UpperF = false;
418
419 struct Suff {
420 static bool has(StringRef suff, StringRef &text) {
421 if (text.endswith(suff)) {
422 text = text.substr(0, text.size()-suff.size());
423 return true;
424 }
425 return false;
426 }
427 };
428
429 while (1) {
430 if (Suff::has("u", text)) {
431 UpperU = false;
432 } else if (Suff::has("U", text)) {
433 UpperU = true;
434 } else if (Suff::has("ll", text)) {
435 UpperL = false;
436 } else if (Suff::has("LL", text)) {
437 UpperL = true;
438 } else if (Suff::has("l", text)) {
439 UpperL = false;
440 } else if (Suff::has("L", text)) {
441 UpperL = true;
442 } else if (isFloat && Suff::has("f", text)) {
443 UpperF = false;
444 } else if (isFloat && Suff::has("F", text)) {
445 UpperF = true;
446 } else
447 break;
448 }
449
450 if (!UpperU.hasValue() && !UpperL.hasValue())
451 UpperU = UpperL = true;
452 else if (UpperU.hasValue() && !UpperL.hasValue())
453 UpperL = UpperU;
454 else if (UpperL.hasValue() && !UpperU.hasValue())
455 UpperU = UpperL;
456
457 Info.U = *UpperU ? "U" : "u";
458 Info.L = *UpperL ? "L" : "l";
459 Info.LL = *UpperL ? "LL" : "ll";
460 Info.F = UpperF ? "F" : "f";
461
462 Info.Hex = Info.Octal = false;
463 if (text.startswith("0x"))
464 Info.Hex = true;
465 else if (!isFloat && !isIntZero && text.startswith("0"))
466 Info.Octal = true;
467
468 SourceLocation B = literalRange.getBegin();
469 Info.WithoutSuffRange =
470 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
471 return true;
472}
473
474static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
475 const NSAPI &NS, Commit &commit) {
476 if (Msg->getNumArgs() != 1)
477 return false;
478
479 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
480 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
481 return rewriteToCharLiteral(Msg, CharE, NS, commit);
482 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
483 return rewriteToBoolLiteral(Msg, BE, NS, commit);
484 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
485 return rewriteToBoolLiteral(Msg, BE, NS, commit);
486
487 const Expr *literalE = Arg;
488 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
489 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
490 literalE = UOE->getSubExpr();
491 }
492
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000493 // Only integer and floating literals, otherwise try to rewrite to boxed
494 // expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000495 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000496 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000497
498 ASTContext &Ctx = NS.getASTContext();
499 Selector Sel = Msg->getSelector();
500 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
501 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
502 if (!MKOpt)
503 return false;
504 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
505
Benjamin Kramerece209a2012-03-13 17:05:43 +0000506 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000507 bool CallIsFloating = false, CallIsDouble = false;
508
509 switch (MK) {
510 // We cannot have these calls with int/float literals.
511 case NSAPI::NSNumberWithChar:
512 case NSAPI::NSNumberWithUnsignedChar:
513 case NSAPI::NSNumberWithShort:
514 case NSAPI::NSNumberWithUnsignedShort:
515 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000516 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000517
518 case NSAPI::NSNumberWithUnsignedInt:
519 case NSAPI::NSNumberWithUnsignedInteger:
520 CallIsUnsigned = true;
521 case NSAPI::NSNumberWithInt:
522 case NSAPI::NSNumberWithInteger:
Ted Kremenekf7639e12012-03-06 20:06:33 +0000523 break;
524
525 case NSAPI::NSNumberWithUnsignedLong:
526 CallIsUnsigned = true;
527 case NSAPI::NSNumberWithLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000528 CallIsLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000529 break;
530
531 case NSAPI::NSNumberWithUnsignedLongLong:
532 CallIsUnsigned = true;
533 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerece209a2012-03-13 17:05:43 +0000534 CallIsLongLong = true;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000535 break;
536
537 case NSAPI::NSNumberWithDouble:
538 CallIsDouble = true;
539 case NSAPI::NSNumberWithFloat:
540 CallIsFloating = true;
541 break;
542 }
543
544 SourceRange ArgRange = Arg->getSourceRange();
545 QualType ArgTy = Arg->getType();
546 QualType CallTy = Msg->getArg(0)->getType();
547
548 // Check for the easy case, the literal maps directly to the call.
549 if (Ctx.hasSameType(ArgTy, CallTy)) {
550 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
551 commit.insert(ArgRange.getBegin(), "@");
552 return true;
553 }
554
555 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000556 // Try with boxed expression if it came from a macro.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000557 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000558 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000559
560 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000561 // For a float passed to integer call, don't try rewriting to objc literal.
562 // It is difficult and a very uncommon case anyway.
563 // But try with boxed expression.
Ted Kremenekf7639e12012-03-06 20:06:33 +0000564 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000565 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000566
567 // Try to modify the literal make it the same type as the method call.
568 // -Modify the suffix, and/or
569 // -Change integer to float
570
571 LiteralInfo LitInfo;
572 bool isIntZero = false;
573 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
574 isIntZero = !IntE->getValue().getBoolValue();
575 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000576 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000577
578 // Not easy to do int -> float with hex/octal and uncommon anyway.
579 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000580 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000581
582 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
583 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
584
585 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
586 LitInfo.WithoutSuffRange);
587 commit.insert(LitB, "@");
588
589 if (!LitIsFloat && CallIsFloating)
590 commit.insert(LitE, ".0");
591
592 if (CallIsFloating) {
593 if (!CallIsDouble)
594 commit.insert(LitE, LitInfo.F);
595 } else {
596 if (CallIsUnsigned)
597 commit.insert(LitE, LitInfo.U);
598
599 if (CallIsLong)
600 commit.insert(LitE, LitInfo.L);
601 else if (CallIsLongLong)
602 commit.insert(LitE, LitInfo.LL);
603 }
604 return true;
605}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000606
Argyrios Kyrtzidis0bbe94f2012-05-14 23:33:49 +0000607// FIXME: Make determination of operator precedence more general and
608// make it broadly available.
609static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
610 const Expr* Expr = FullExpr->IgnoreImpCasts();
611 if (isa<ArraySubscriptExpr>(Expr) ||
612 isa<CallExpr>(Expr) ||
613 isa<DeclRefExpr>(Expr) ||
614 isa<CXXNamedCastExpr>(Expr) ||
615 isa<CXXConstructExpr>(Expr) ||
616 isa<CXXThisExpr>(Expr) ||
617 isa<CXXTypeidExpr>(Expr) ||
618 isa<CXXUnresolvedConstructExpr>(Expr) ||
619 isa<ObjCMessageExpr>(Expr) ||
620 isa<ObjCPropertyRefExpr>(Expr) ||
621 isa<ObjCProtocolExpr>(Expr) ||
622 isa<MemberExpr>(Expr) ||
623 isa<ParenExpr>(FullExpr) ||
624 isa<ParenListExpr>(Expr) ||
625 isa<SizeOfPackExpr>(Expr))
626 return false;
627
628 return true;
629}
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000630static bool castOperatorNeedsParens(const Expr *FullExpr) {
631 const Expr* Expr = FullExpr->IgnoreImpCasts();
632 if (isa<ArraySubscriptExpr>(Expr) ||
633 isa<CallExpr>(Expr) ||
634 isa<DeclRefExpr>(Expr) ||
635 isa<CastExpr>(Expr) ||
636 isa<CXXNewExpr>(Expr) ||
637 isa<CXXConstructExpr>(Expr) ||
638 isa<CXXDeleteExpr>(Expr) ||
639 isa<CXXNoexceptExpr>(Expr) ||
640 isa<CXXPseudoDestructorExpr>(Expr) ||
641 isa<CXXScalarValueInitExpr>(Expr) ||
642 isa<CXXThisExpr>(Expr) ||
643 isa<CXXTypeidExpr>(Expr) ||
644 isa<CXXUnresolvedConstructExpr>(Expr) ||
645 isa<ObjCMessageExpr>(Expr) ||
646 isa<ObjCPropertyRefExpr>(Expr) ||
647 isa<ObjCProtocolExpr>(Expr) ||
648 isa<MemberExpr>(Expr) ||
649 isa<ParenExpr>(FullExpr) ||
650 isa<ParenListExpr>(Expr) ||
651 isa<SizeOfPackExpr>(Expr) ||
652 isa<UnaryOperator>(Expr))
653 return false;
654
655 return true;
656}
657
658static void objectifyExpr(const Expr *E, Commit &commit) {
659 if (!E) return;
660
661 QualType T = E->getType();
662 if (T->isObjCObjectPointerType()) {
663 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
664 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
665 return;
666 } else {
667 return;
668 }
669 } else if (!T->isPointerType()) {
670 return;
671 }
672
673 SourceRange Range = E->getSourceRange();
674 if (castOperatorNeedsParens(E))
675 commit.insertWrap("(", Range, ")");
676 commit.insertBefore(Range.getBegin(), "(id)");
677}
Argyrios Kyrtzidis491e4ae2012-05-15 19:17:49 +0000678
679//===----------------------------------------------------------------------===//
680// rewriteToNumericBoxedExpression.
681//===----------------------------------------------------------------------===//
682
683static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
684 const NSAPI &NS, Commit &commit) {
685 if (Msg->getNumArgs() != 1)
686 return false;
687
688 const Expr *Arg = Msg->getArg(0);
689 if (Arg->isTypeDependent())
690 return false;
691
692 ASTContext &Ctx = NS.getASTContext();
693 Selector Sel = Msg->getSelector();
694 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
695 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
696 if (!MKOpt)
697 return false;
698 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
699
700 const Expr *OrigArg = Arg->IgnoreImpCasts();
701 QualType FinalTy = Arg->getType();
702 QualType OrigTy = OrigArg->getType();
703 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
704 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
705
706 bool isTruncated = FinalTySize < OrigTySize;
707 bool needsCast = false;
708
709 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
710 switch (ICE->getCastKind()) {
711 case CK_LValueToRValue:
712 case CK_NoOp:
713 case CK_UserDefinedConversion:
714 break;
715
716 case CK_IntegralCast: {
717 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
718 break;
719 // Be more liberal with Integer/UnsignedInteger which are very commonly
720 // used.
721 if ((MK == NSAPI::NSNumberWithInteger ||
722 MK == NSAPI::NSNumberWithUnsignedInteger) &&
723 !isTruncated) {
724 if (OrigTy->getAs<EnumType>())
725 break;
726 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
727 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
728 break;
729 }
730
731 needsCast = true;
732 break;
733 }
734
735 case CK_PointerToBoolean:
736 case CK_IntegralToBoolean:
737 case CK_IntegralToFloating:
738 case CK_FloatingToIntegral:
739 case CK_FloatingToBoolean:
740 case CK_FloatingCast:
741 case CK_FloatingComplexToReal:
742 case CK_FloatingComplexToBoolean:
743 case CK_IntegralComplexToReal:
744 case CK_IntegralComplexToBoolean:
745 case CK_AtomicToNonAtomic:
746 needsCast = true;
747 break;
748
749 case CK_Dependent:
750 case CK_BitCast:
751 case CK_LValueBitCast:
752 case CK_BaseToDerived:
753 case CK_DerivedToBase:
754 case CK_UncheckedDerivedToBase:
755 case CK_Dynamic:
756 case CK_ToUnion:
757 case CK_ArrayToPointerDecay:
758 case CK_FunctionToPointerDecay:
759 case CK_NullToPointer:
760 case CK_NullToMemberPointer:
761 case CK_BaseToDerivedMemberPointer:
762 case CK_DerivedToBaseMemberPointer:
763 case CK_MemberPointerToBoolean:
764 case CK_ReinterpretMemberPointer:
765 case CK_ConstructorConversion:
766 case CK_IntegralToPointer:
767 case CK_PointerToIntegral:
768 case CK_ToVoid:
769 case CK_VectorSplat:
770 case CK_CPointerToObjCPointerCast:
771 case CK_BlockPointerToObjCPointerCast:
772 case CK_AnyPointerToBlockPointerCast:
773 case CK_ObjCObjectLValueCast:
774 case CK_FloatingRealToComplex:
775 case CK_FloatingComplexCast:
776 case CK_FloatingComplexToIntegralComplex:
777 case CK_IntegralRealToComplex:
778 case CK_IntegralComplexCast:
779 case CK_IntegralComplexToFloatingComplex:
780 case CK_ARCProduceObject:
781 case CK_ARCConsumeObject:
782 case CK_ARCReclaimReturnedObject:
783 case CK_ARCExtendBlockObject:
784 case CK_NonAtomicToAtomic:
785 case CK_CopyAndAutoreleaseBlockObject:
786 return false;
787 }
788 }
789
790 if (needsCast)
791 return false;
792
793 SourceRange ArgRange = OrigArg->getSourceRange();
794 commit.replaceWithInner(Msg->getSourceRange(), OrigArg->getSourceRange());
795
796 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
797 commit.insertBefore(ArgRange.getBegin(), "@");
798 else
799 commit.insertWrap("@(", ArgRange, ")");
800
801 return true;
802}