blob: 7445700cc49ff124cfbaa491c550e13dc1301a28 [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,
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 Kyrtzidis20119a82012-05-14 23:33:49 +000080static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
81
Ted Kremenek30660a82012-03-06 20:06:33 +000082static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +000083 if (subscriptOperatorNeedsParens(Receiver)) {
Ted Kremenek30660a82012-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 Kyrtzidis0d578a62012-05-15 19:17:49 +0000212static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
213 const NSAPI &NS, Commit &commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000214static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
215 const NSAPI &NS, Commit &commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000216
217bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
218 const NSAPI &NS, Commit &commit) {
219 IdentifierInfo *II = 0;
220 if (!checkForLiteralCreation(Msg, II))
221 return false;
222
223 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
224 return rewriteToArrayLiteral(Msg, NS, commit);
225 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
226 return rewriteToDictionaryLiteral(Msg, NS, commit);
227 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
228 return rewriteToNumberLiteral(Msg, NS, commit);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000229 if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
230 return rewriteToStringBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000231
232 return false;
233}
234
235//===----------------------------------------------------------------------===//
236// rewriteToArrayLiteral.
237//===----------------------------------------------------------------------===//
238
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000239/// \brief Adds an explicit cast to 'id' if the type is not objc object.
240static void objectifyExpr(const Expr *E, Commit &commit);
241
Ted Kremenek30660a82012-03-06 20:06:33 +0000242static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
243 const NSAPI &NS, Commit &commit) {
244 Selector Sel = Msg->getSelector();
245 SourceRange MsgRange = Msg->getSourceRange();
246
247 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
248 if (Msg->getNumArgs() != 0)
249 return false;
250 commit.replace(MsgRange, "@[]");
251 return true;
252 }
253
254 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
255 if (Msg->getNumArgs() != 1)
256 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000257 objectifyExpr(Msg->getArg(0), commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000258 SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
259 commit.replaceWithInner(MsgRange, ArgRange);
260 commit.insertWrap("@[", ArgRange, "]");
261 return true;
262 }
263
264 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) {
265 if (Msg->getNumArgs() == 0)
266 return false;
267 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
268 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
269 return false;
270
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000271 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
272 objectifyExpr(Msg->getArg(i), commit);
273
Ted Kremenek30660a82012-03-06 20:06:33 +0000274 if (Msg->getNumArgs() == 1) {
275 commit.replace(MsgRange, "@[]");
276 return true;
277 }
278 SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
279 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
280 commit.replaceWithInner(MsgRange, ArgRange);
281 commit.insertWrap("@[", ArgRange, "]");
282 return true;
283 }
284
285 return false;
286}
287
288//===----------------------------------------------------------------------===//
289// rewriteToDictionaryLiteral.
290//===----------------------------------------------------------------------===//
291
292static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
293 const NSAPI &NS, Commit &commit) {
294 Selector Sel = Msg->getSelector();
295 SourceRange MsgRange = Msg->getSourceRange();
296
297 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
298 if (Msg->getNumArgs() != 0)
299 return false;
300 commit.replace(MsgRange, "@{}");
301 return true;
302 }
303
304 if (Sel == NS.getNSDictionarySelector(
305 NSAPI::NSDict_dictionaryWithObjectForKey)) {
306 if (Msg->getNumArgs() != 2)
307 return false;
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000308
309 objectifyExpr(Msg->getArg(0), commit);
310 objectifyExpr(Msg->getArg(1), commit);
311
Ted Kremenek30660a82012-03-06 20:06:33 +0000312 SourceRange ValRange = Msg->getArg(0)->getSourceRange();
313 SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
314 // Insert key before the value.
315 commit.insertBefore(ValRange.getBegin(), ": ");
316 commit.insertFromRange(ValRange.getBegin(),
317 CharSourceRange::getTokenRange(KeyRange),
318 /*afterToken=*/false, /*beforePreviousInsertions=*/true);
319 commit.insertBefore(ValRange.getBegin(), "@{");
320 commit.insertAfterToken(ValRange.getEnd(), "}");
321 commit.replaceWithInner(MsgRange, ValRange);
322 return true;
323 }
324
325 if (Sel == NS.getNSDictionarySelector(
326 NSAPI::NSDict_dictionaryWithObjectsAndKeys)) {
327 if (Msg->getNumArgs() % 2 != 1)
328 return false;
329 unsigned SentinelIdx = Msg->getNumArgs() - 1;
330 const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
331 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
332 return false;
333
334 if (Msg->getNumArgs() == 1) {
335 commit.replace(MsgRange, "@{}");
336 return true;
337 }
338
339 for (unsigned i = 0; i < SentinelIdx; i += 2) {
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000340 objectifyExpr(Msg->getArg(i), commit);
341 objectifyExpr(Msg->getArg(i+1), commit);
342
Ted Kremenek30660a82012-03-06 20:06:33 +0000343 SourceRange ValRange = Msg->getArg(i)->getSourceRange();
344 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
345 // Insert value after key.
346 commit.insertAfterToken(KeyRange.getEnd(), ": ");
347 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
348 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
349 KeyRange.getBegin()));
350 }
351 // Range of arguments up until and including the last key.
352 // The sentinel and first value are cut off, the value will move after the
353 // key.
354 SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
355 Msg->getArg(SentinelIdx-1)->getLocEnd());
356 commit.insertWrap("@{", ArgRange, "}");
357 commit.replaceWithInner(MsgRange, ArgRange);
358 return true;
359 }
360
361 return false;
362}
363
364//===----------------------------------------------------------------------===//
365// rewriteToNumberLiteral.
366//===----------------------------------------------------------------------===//
367
368static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
369 const CharacterLiteral *Arg,
370 const NSAPI &NS, Commit &commit) {
371 if (Arg->getKind() != CharacterLiteral::Ascii)
372 return false;
373 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
374 Msg->getSelector())) {
375 SourceRange ArgRange = Arg->getSourceRange();
376 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
377 commit.insert(ArgRange.getBegin(), "@");
378 return true;
379 }
380
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000381 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000382}
383
384static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
385 const Expr *Arg,
386 const NSAPI &NS, Commit &commit) {
387 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
388 Msg->getSelector())) {
389 SourceRange ArgRange = Arg->getSourceRange();
390 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
391 commit.insert(ArgRange.getBegin(), "@");
392 return true;
393 }
394
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000395 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000396}
397
398namespace {
399
400struct LiteralInfo {
401 bool Hex, Octal;
402 StringRef U, F, L, LL;
403 CharSourceRange WithoutSuffRange;
404};
405
406}
407
408static bool getLiteralInfo(SourceRange literalRange,
409 bool isFloat, bool isIntZero,
410 ASTContext &Ctx, LiteralInfo &Info) {
411 if (literalRange.getBegin().isMacroID() ||
412 literalRange.getEnd().isMacroID())
413 return false;
414 StringRef text = Lexer::getSourceText(
415 CharSourceRange::getTokenRange(literalRange),
David Blaikie4e4d0842012-03-11 07:00:24 +0000416 Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000417 if (text.empty())
418 return false;
419
420 llvm::Optional<bool> UpperU, UpperL;
421 bool UpperF = false;
422
423 struct Suff {
424 static bool has(StringRef suff, StringRef &text) {
425 if (text.endswith(suff)) {
426 text = text.substr(0, text.size()-suff.size());
427 return true;
428 }
429 return false;
430 }
431 };
432
433 while (1) {
434 if (Suff::has("u", text)) {
435 UpperU = false;
436 } else if (Suff::has("U", text)) {
437 UpperU = true;
438 } else if (Suff::has("ll", text)) {
439 UpperL = false;
440 } else if (Suff::has("LL", text)) {
441 UpperL = true;
442 } else if (Suff::has("l", text)) {
443 UpperL = false;
444 } else if (Suff::has("L", text)) {
445 UpperL = true;
446 } else if (isFloat && Suff::has("f", text)) {
447 UpperF = false;
448 } else if (isFloat && Suff::has("F", text)) {
449 UpperF = true;
450 } else
451 break;
452 }
453
454 if (!UpperU.hasValue() && !UpperL.hasValue())
455 UpperU = UpperL = true;
456 else if (UpperU.hasValue() && !UpperL.hasValue())
457 UpperL = UpperU;
458 else if (UpperL.hasValue() && !UpperU.hasValue())
459 UpperU = UpperL;
460
461 Info.U = *UpperU ? "U" : "u";
462 Info.L = *UpperL ? "L" : "l";
463 Info.LL = *UpperL ? "LL" : "ll";
464 Info.F = UpperF ? "F" : "f";
465
466 Info.Hex = Info.Octal = false;
467 if (text.startswith("0x"))
468 Info.Hex = true;
469 else if (!isFloat && !isIntZero && text.startswith("0"))
470 Info.Octal = true;
471
472 SourceLocation B = literalRange.getBegin();
473 Info.WithoutSuffRange =
474 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
475 return true;
476}
477
478static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
479 const NSAPI &NS, Commit &commit) {
480 if (Msg->getNumArgs() != 1)
481 return false;
482
483 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
484 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
485 return rewriteToCharLiteral(Msg, CharE, NS, commit);
486 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
487 return rewriteToBoolLiteral(Msg, BE, NS, commit);
488 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
489 return rewriteToBoolLiteral(Msg, BE, NS, commit);
490
491 const Expr *literalE = Arg;
492 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
493 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
494 literalE = UOE->getSubExpr();
495 }
496
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000497 // Only integer and floating literals, otherwise try to rewrite to boxed
498 // expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000499 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000500 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000501
502 ASTContext &Ctx = NS.getASTContext();
503 Selector Sel = Msg->getSelector();
504 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
505 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
506 if (!MKOpt)
507 return false;
508 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
509
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000510 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
Ted Kremenek30660a82012-03-06 20:06:33 +0000511 bool CallIsFloating = false, CallIsDouble = false;
512
513 switch (MK) {
514 // We cannot have these calls with int/float literals.
515 case NSAPI::NSNumberWithChar:
516 case NSAPI::NSNumberWithUnsignedChar:
517 case NSAPI::NSNumberWithShort:
518 case NSAPI::NSNumberWithUnsignedShort:
519 case NSAPI::NSNumberWithBool:
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000520 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000521
522 case NSAPI::NSNumberWithUnsignedInt:
523 case NSAPI::NSNumberWithUnsignedInteger:
524 CallIsUnsigned = true;
525 case NSAPI::NSNumberWithInt:
526 case NSAPI::NSNumberWithInteger:
Ted Kremenek30660a82012-03-06 20:06:33 +0000527 break;
528
529 case NSAPI::NSNumberWithUnsignedLong:
530 CallIsUnsigned = true;
531 case NSAPI::NSNumberWithLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000532 CallIsLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000533 break;
534
535 case NSAPI::NSNumberWithUnsignedLongLong:
536 CallIsUnsigned = true;
537 case NSAPI::NSNumberWithLongLong:
Benjamin Kramerbea6c0a2012-03-13 17:05:43 +0000538 CallIsLongLong = true;
Ted Kremenek30660a82012-03-06 20:06:33 +0000539 break;
540
541 case NSAPI::NSNumberWithDouble:
542 CallIsDouble = true;
543 case NSAPI::NSNumberWithFloat:
544 CallIsFloating = true;
545 break;
546 }
547
548 SourceRange ArgRange = Arg->getSourceRange();
549 QualType ArgTy = Arg->getType();
550 QualType CallTy = Msg->getArg(0)->getType();
551
552 // Check for the easy case, the literal maps directly to the call.
553 if (Ctx.hasSameType(ArgTy, CallTy)) {
554 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
555 commit.insert(ArgRange.getBegin(), "@");
556 return true;
557 }
558
559 // We will need to modify the literal suffix to get the same type as the call.
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000560 // Try with boxed expression if it came from a macro.
Ted Kremenek30660a82012-03-06 20:06:33 +0000561 if (ArgRange.getBegin().isMacroID())
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000562 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000563
564 bool LitIsFloat = ArgTy->isFloatingType();
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000565 // For a float passed to integer call, don't try rewriting to objc literal.
566 // It is difficult and a very uncommon case anyway.
567 // But try with boxed expression.
Ted Kremenek30660a82012-03-06 20:06:33 +0000568 if (LitIsFloat && !CallIsFloating)
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000569 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000570
571 // Try to modify the literal make it the same type as the method call.
572 // -Modify the suffix, and/or
573 // -Change integer to float
574
575 LiteralInfo LitInfo;
576 bool isIntZero = false;
577 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
578 isIntZero = !IntE->getValue().getBoolValue();
579 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000580 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000581
582 // Not easy to do int -> float with hex/octal and uncommon anyway.
583 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000584 return rewriteToNumericBoxedExpression(Msg, NS, commit);
Ted Kremenek30660a82012-03-06 20:06:33 +0000585
586 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
587 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
588
589 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
590 LitInfo.WithoutSuffRange);
591 commit.insert(LitB, "@");
592
593 if (!LitIsFloat && CallIsFloating)
594 commit.insert(LitE, ".0");
595
596 if (CallIsFloating) {
597 if (!CallIsDouble)
598 commit.insert(LitE, LitInfo.F);
599 } else {
600 if (CallIsUnsigned)
601 commit.insert(LitE, LitInfo.U);
602
603 if (CallIsLong)
604 commit.insert(LitE, LitInfo.L);
605 else if (CallIsLongLong)
606 commit.insert(LitE, LitInfo.LL);
607 }
608 return true;
609}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000610
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000611// FIXME: Make determination of operator precedence more general and
612// make it broadly available.
613static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
614 const Expr* Expr = FullExpr->IgnoreImpCasts();
615 if (isa<ArraySubscriptExpr>(Expr) ||
616 isa<CallExpr>(Expr) ||
617 isa<DeclRefExpr>(Expr) ||
618 isa<CXXNamedCastExpr>(Expr) ||
619 isa<CXXConstructExpr>(Expr) ||
620 isa<CXXThisExpr>(Expr) ||
621 isa<CXXTypeidExpr>(Expr) ||
622 isa<CXXUnresolvedConstructExpr>(Expr) ||
623 isa<ObjCMessageExpr>(Expr) ||
624 isa<ObjCPropertyRefExpr>(Expr) ||
625 isa<ObjCProtocolExpr>(Expr) ||
626 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000627 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis20119a82012-05-14 23:33:49 +0000628 isa<ParenExpr>(FullExpr) ||
629 isa<ParenListExpr>(Expr) ||
630 isa<SizeOfPackExpr>(Expr))
631 return false;
632
633 return true;
634}
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000635static bool castOperatorNeedsParens(const Expr *FullExpr) {
636 const Expr* Expr = FullExpr->IgnoreImpCasts();
637 if (isa<ArraySubscriptExpr>(Expr) ||
638 isa<CallExpr>(Expr) ||
639 isa<DeclRefExpr>(Expr) ||
640 isa<CastExpr>(Expr) ||
641 isa<CXXNewExpr>(Expr) ||
642 isa<CXXConstructExpr>(Expr) ||
643 isa<CXXDeleteExpr>(Expr) ||
644 isa<CXXNoexceptExpr>(Expr) ||
645 isa<CXXPseudoDestructorExpr>(Expr) ||
646 isa<CXXScalarValueInitExpr>(Expr) ||
647 isa<CXXThisExpr>(Expr) ||
648 isa<CXXTypeidExpr>(Expr) ||
649 isa<CXXUnresolvedConstructExpr>(Expr) ||
650 isa<ObjCMessageExpr>(Expr) ||
651 isa<ObjCPropertyRefExpr>(Expr) ||
652 isa<ObjCProtocolExpr>(Expr) ||
653 isa<MemberExpr>(Expr) ||
Argyrios Kyrtzidis2bddd432012-05-22 00:47:53 +0000654 isa<ObjCIvarRefExpr>(Expr) ||
Argyrios Kyrtzidis055b3952012-05-14 22:01:53 +0000655 isa<ParenExpr>(FullExpr) ||
656 isa<ParenListExpr>(Expr) ||
657 isa<SizeOfPackExpr>(Expr) ||
658 isa<UnaryOperator>(Expr))
659 return false;
660
661 return true;
662}
663
664static void objectifyExpr(const Expr *E, Commit &commit) {
665 if (!E) return;
666
667 QualType T = E->getType();
668 if (T->isObjCObjectPointerType()) {
669 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
670 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
671 return;
672 } else {
673 return;
674 }
675 } else if (!T->isPointerType()) {
676 return;
677 }
678
679 SourceRange Range = E->getSourceRange();
680 if (castOperatorNeedsParens(E))
681 commit.insertWrap("(", Range, ")");
682 commit.insertBefore(Range.getBegin(), "(id)");
683}
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000684
685//===----------------------------------------------------------------------===//
686// rewriteToNumericBoxedExpression.
687//===----------------------------------------------------------------------===//
688
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000689static bool isEnumConstant(const Expr *E) {
690 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
691 if (const ValueDecl *VD = DRE->getDecl())
692 return isa<EnumConstantDecl>(VD);
693
694 return false;
695}
696
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000697static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
698 const NSAPI &NS, Commit &commit) {
699 if (Msg->getNumArgs() != 1)
700 return false;
701
702 const Expr *Arg = Msg->getArg(0);
703 if (Arg->isTypeDependent())
704 return false;
705
706 ASTContext &Ctx = NS.getASTContext();
707 Selector Sel = Msg->getSelector();
708 llvm::Optional<NSAPI::NSNumberLiteralMethodKind>
709 MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
710 if (!MKOpt)
711 return false;
712 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
713
714 const Expr *OrigArg = Arg->IgnoreImpCasts();
715 QualType FinalTy = Arg->getType();
716 QualType OrigTy = OrigArg->getType();
717 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
718 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
719
720 bool isTruncated = FinalTySize < OrigTySize;
721 bool needsCast = false;
722
723 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
724 switch (ICE->getCastKind()) {
725 case CK_LValueToRValue:
726 case CK_NoOp:
727 case CK_UserDefinedConversion:
728 break;
729
730 case CK_IntegralCast: {
731 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
732 break;
733 // Be more liberal with Integer/UnsignedInteger which are very commonly
734 // used.
735 if ((MK == NSAPI::NSNumberWithInteger ||
736 MK == NSAPI::NSNumberWithUnsignedInteger) &&
737 !isTruncated) {
Argyrios Kyrtzidisc5d33e92012-05-15 22:59:54 +0000738 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000739 break;
740 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
741 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
742 break;
743 }
744
745 needsCast = true;
746 break;
747 }
748
749 case CK_PointerToBoolean:
750 case CK_IntegralToBoolean:
751 case CK_IntegralToFloating:
752 case CK_FloatingToIntegral:
753 case CK_FloatingToBoolean:
754 case CK_FloatingCast:
755 case CK_FloatingComplexToReal:
756 case CK_FloatingComplexToBoolean:
757 case CK_IntegralComplexToReal:
758 case CK_IntegralComplexToBoolean:
759 case CK_AtomicToNonAtomic:
760 needsCast = true;
761 break;
762
763 case CK_Dependent:
764 case CK_BitCast:
765 case CK_LValueBitCast:
766 case CK_BaseToDerived:
767 case CK_DerivedToBase:
768 case CK_UncheckedDerivedToBase:
769 case CK_Dynamic:
770 case CK_ToUnion:
771 case CK_ArrayToPointerDecay:
772 case CK_FunctionToPointerDecay:
773 case CK_NullToPointer:
774 case CK_NullToMemberPointer:
775 case CK_BaseToDerivedMemberPointer:
776 case CK_DerivedToBaseMemberPointer:
777 case CK_MemberPointerToBoolean:
778 case CK_ReinterpretMemberPointer:
779 case CK_ConstructorConversion:
780 case CK_IntegralToPointer:
781 case CK_PointerToIntegral:
782 case CK_ToVoid:
783 case CK_VectorSplat:
784 case CK_CPointerToObjCPointerCast:
785 case CK_BlockPointerToObjCPointerCast:
786 case CK_AnyPointerToBlockPointerCast:
787 case CK_ObjCObjectLValueCast:
788 case CK_FloatingRealToComplex:
789 case CK_FloatingComplexCast:
790 case CK_FloatingComplexToIntegralComplex:
791 case CK_IntegralRealToComplex:
792 case CK_IntegralComplexCast:
793 case CK_IntegralComplexToFloatingComplex:
794 case CK_ARCProduceObject:
795 case CK_ARCConsumeObject:
796 case CK_ARCReclaimReturnedObject:
797 case CK_ARCExtendBlockObject:
798 case CK_NonAtomicToAtomic:
799 case CK_CopyAndAutoreleaseBlockObject:
800 return false;
801 }
802 }
803
804 if (needsCast)
805 return false;
806
807 SourceRange ArgRange = OrigArg->getSourceRange();
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000808 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
Argyrios Kyrtzidis0d578a62012-05-15 19:17:49 +0000809
810 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
811 commit.insertBefore(ArgRange.getBegin(), "@");
812 else
813 commit.insertWrap("@(", ArgRange, ")");
814
815 return true;
816}
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000817
818//===----------------------------------------------------------------------===//
819// rewriteToStringBoxedExpression.
820//===----------------------------------------------------------------------===//
821
822static bool doRewriteToUTF8StringBoxedExpressionHelper(
823 const ObjCMessageExpr *Msg,
824 const NSAPI &NS, Commit &commit) {
825 const Expr *Arg = Msg->getArg(0);
826 if (Arg->isTypeDependent())
827 return false;
828
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000829 ASTContext &Ctx = NS.getASTContext();
830
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000831 const Expr *OrigArg = Arg->IgnoreImpCasts();
832 QualType OrigTy = OrigArg->getType();
Argyrios Kyrtzidis07736592012-05-16 00:21:21 +0000833 if (OrigTy->isArrayType())
834 OrigTy = Ctx.getArrayDecayedType(OrigTy);
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000835
836 if (const StringLiteral *
837 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
838 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
839 commit.insert(StrE->getLocStart(), "@");
840 return true;
841 }
842
Argyrios Kyrtzidis7fe103c2012-05-15 22:22:10 +0000843 if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
844 QualType PointeeType = PT->getPointeeType();
845 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
846 SourceRange ArgRange = OrigArg->getSourceRange();
847 commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
848
849 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
850 commit.insertBefore(ArgRange.getBegin(), "@");
851 else
852 commit.insertWrap("@(", ArgRange, ")");
853
854 return true;
855 }
856 }
857
858 return false;
859}
860
861static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
862 const NSAPI &NS, Commit &commit) {
863 Selector Sel = Msg->getSelector();
864
865 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
866 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) {
867 if (Msg->getNumArgs() != 1)
868 return false;
869 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
870 }
871
872 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
873 if (Msg->getNumArgs() != 2)
874 return false;
875
876 const Expr *encodingArg = Msg->getArg(1);
877 if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
878 NS.isNSASCIIStringEncodingConstant(encodingArg))
879 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
880 }
881
882 return false;
883}