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