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