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