Home | History | Annotate | Download | only in Edit
      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/AST/ASTContext.h"
     16 #include "clang/AST/ExprCXX.h"
     17 #include "clang/AST/ExprObjC.h"
     18 #include "clang/AST/NSAPI.h"
     19 #include "clang/AST/ParentMap.h"
     20 #include "clang/Edit/Commit.h"
     21 #include "clang/Lex/Lexer.h"
     22 
     23 using namespace clang;
     24 using namespace edit;
     25 
     26 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
     27                                     IdentifierInfo *&ClassId,
     28                                     const LangOptions &LangOpts) {
     29   if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
     30     return false;
     31 
     32   const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
     33   if (!Receiver)
     34     return false;
     35   ClassId = Receiver->getIdentifier();
     36 
     37   if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
     38     return true;
     39 
     40   // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
     41   // since the change from +1 to +0 will be handled fine by ARC.
     42   if (LangOpts.ObjCAutoRefCount) {
     43     if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
     44       if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
     45                            Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
     46         if (Rec->getMethodFamily() == OMF_alloc)
     47           return true;
     48       }
     49     }
     50   }
     51 
     52   return false;
     53 }
     54 
     55 //===----------------------------------------------------------------------===//
     56 // rewriteObjCRedundantCallWithLiteral.
     57 //===----------------------------------------------------------------------===//
     58 
     59 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
     60                                               const NSAPI &NS, Commit &commit) {
     61   IdentifierInfo *II = nullptr;
     62   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
     63     return false;
     64   if (Msg->getNumArgs() != 1)
     65     return false;
     66 
     67   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
     68   Selector Sel = Msg->getSelector();
     69 
     70   if ((isa<ObjCStringLiteral>(Arg) &&
     71        NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
     72        (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
     73         NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
     74 
     75       (isa<ObjCArrayLiteral>(Arg) &&
     76        NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
     77        (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
     78         NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
     79 
     80       (isa<ObjCDictionaryLiteral>(Arg) &&
     81        NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
     82        (NS.getNSDictionarySelector(
     83                               NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
     84         NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
     85 
     86     commit.replaceWithInner(Msg->getSourceRange(),
     87                            Msg->getArg(0)->getSourceRange());
     88     return true;
     89   }
     90 
     91   return false;
     92 }
     93 
     94 //===----------------------------------------------------------------------===//
     95 // rewriteToObjCSubscriptSyntax.
     96 //===----------------------------------------------------------------------===//
     97 
     98 /// \brief Check for classes that accept 'objectForKey:' (or the other selectors
     99 /// that the migrator handles) but return their instances as 'id', resulting
    100 /// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
    101 ///
    102 /// When checking if we can convert to subscripting syntax, check whether
    103 /// the receiver is a result of a class method from a hardcoded list of
    104 /// such classes. In such a case return the specific class as the interface
    105 /// of the receiver.
    106 ///
    107 /// FIXME: Remove this when these classes start using 'instancetype'.
    108 static const ObjCInterfaceDecl *
    109 maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
    110                                          const Expr *Receiver,
    111                                          ASTContext &Ctx) {
    112   assert(IFace && Receiver);
    113 
    114   // If the receiver has type 'id'...
    115   if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
    116     return IFace;
    117 
    118   const ObjCMessageExpr *
    119     InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
    120   if (!InnerMsg)
    121     return IFace;
    122 
    123   QualType ClassRec;
    124   switch (InnerMsg->getReceiverKind()) {
    125   case ObjCMessageExpr::Instance:
    126   case ObjCMessageExpr::SuperInstance:
    127     return IFace;
    128 
    129   case ObjCMessageExpr::Class:
    130     ClassRec = InnerMsg->getClassReceiver();
    131     break;
    132   case ObjCMessageExpr::SuperClass:
    133     ClassRec = InnerMsg->getSuperType();
    134     break;
    135   }
    136 
    137   if (ClassRec.isNull())
    138     return IFace;
    139 
    140   // ...and it is the result of a class message...
    141 
    142   const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
    143   if (!ObjTy)
    144     return IFace;
    145   const ObjCInterfaceDecl *OID = ObjTy->getInterface();
    146 
    147   // ...and the receiving class is NSMapTable or NSLocale, return that
    148   // class as the receiving interface.
    149   if (OID->getName() == "NSMapTable" ||
    150       OID->getName() == "NSLocale")
    151     return OID;
    152 
    153   return IFace;
    154 }
    155 
    156 static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
    157                                         const ObjCMessageExpr *Msg,
    158                                         ASTContext &Ctx,
    159                                         Selector subscriptSel) {
    160   const Expr *Rec = Msg->getInstanceReceiver();
    161   if (!Rec)
    162     return false;
    163   IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
    164 
    165   if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
    166     if (!MD->isUnavailable())
    167       return true;
    168   }
    169   return false;
    170 }
    171 
    172 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
    173 
    174 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
    175   if (subscriptOperatorNeedsParens(Receiver)) {
    176     SourceRange RecRange = Receiver->getSourceRange();
    177     commit.insertWrap("(", RecRange, ")");
    178   }
    179 }
    180 
    181 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
    182                                         Commit &commit) {
    183   if (Msg->getNumArgs() != 1)
    184     return false;
    185   const Expr *Rec = Msg->getInstanceReceiver();
    186   if (!Rec)
    187     return false;
    188 
    189   SourceRange MsgRange = Msg->getSourceRange();
    190   SourceRange RecRange = Rec->getSourceRange();
    191   SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
    192 
    193   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
    194                                                        ArgRange.getBegin()),
    195                          CharSourceRange::getTokenRange(RecRange));
    196   commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
    197                          ArgRange);
    198   commit.insertWrap("[", ArgRange, "]");
    199   maybePutParensOnReceiver(Rec, commit);
    200   return true;
    201 }
    202 
    203 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
    204                                        const ObjCMessageExpr *Msg,
    205                                        const NSAPI &NS,
    206                                        Commit &commit) {
    207   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
    208                                    NS.getObjectAtIndexedSubscriptSelector()))
    209     return false;
    210   return rewriteToSubscriptGetCommon(Msg, commit);
    211 }
    212 
    213 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
    214                                             const ObjCMessageExpr *Msg,
    215                                             const NSAPI &NS,
    216                                             Commit &commit) {
    217   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
    218                                   NS.getObjectForKeyedSubscriptSelector()))
    219     return false;
    220   return rewriteToSubscriptGetCommon(Msg, commit);
    221 }
    222 
    223 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
    224                                        const ObjCMessageExpr *Msg,
    225                                        const NSAPI &NS,
    226                                        Commit &commit) {
    227   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
    228                                    NS.getSetObjectAtIndexedSubscriptSelector()))
    229     return false;
    230 
    231   if (Msg->getNumArgs() != 2)
    232     return false;
    233   const Expr *Rec = Msg->getInstanceReceiver();
    234   if (!Rec)
    235     return false;
    236 
    237   SourceRange MsgRange = Msg->getSourceRange();
    238   SourceRange RecRange = Rec->getSourceRange();
    239   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
    240   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
    241 
    242   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
    243                                                        Arg0Range.getBegin()),
    244                          CharSourceRange::getTokenRange(RecRange));
    245   commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
    246                                                        Arg1Range.getBegin()),
    247                          CharSourceRange::getTokenRange(Arg0Range));
    248   commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
    249                          Arg1Range);
    250   commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
    251                                                        Arg1Range.getBegin()),
    252                     "] = ");
    253   maybePutParensOnReceiver(Rec, commit);
    254   return true;
    255 }
    256 
    257 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
    258                                             const ObjCMessageExpr *Msg,
    259                                             const NSAPI &NS,
    260                                             Commit &commit) {
    261   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
    262                                    NS.getSetObjectForKeyedSubscriptSelector()))
    263     return false;
    264 
    265   if (Msg->getNumArgs() != 2)
    266     return false;
    267   const Expr *Rec = Msg->getInstanceReceiver();
    268   if (!Rec)
    269     return false;
    270 
    271   SourceRange MsgRange = Msg->getSourceRange();
    272   SourceRange RecRange = Rec->getSourceRange();
    273   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
    274   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
    275 
    276   SourceLocation LocBeforeVal = Arg0Range.getBegin();
    277   commit.insertBefore(LocBeforeVal, "] = ");
    278   commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
    279                          /*beforePreviousInsertions=*/true);
    280   commit.insertBefore(LocBeforeVal, "[");
    281   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
    282                                                        Arg0Range.getBegin()),
    283                          CharSourceRange::getTokenRange(RecRange));
    284   commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
    285                          Arg0Range);
    286   maybePutParensOnReceiver(Rec, commit);
    287   return true;
    288 }
    289 
    290 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
    291                                         const NSAPI &NS, Commit &commit) {
    292   if (!Msg || Msg->isImplicit() ||
    293       Msg->getReceiverKind() != ObjCMessageExpr::Instance)
    294     return false;
    295   const ObjCMethodDecl *Method = Msg->getMethodDecl();
    296   if (!Method)
    297     return false;
    298 
    299   const ObjCInterfaceDecl *IFace =
    300       NS.getASTContext().getObjContainingInterface(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 
    327 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
    328                                   const NSAPI &NS, Commit &commit,
    329                                   const ParentMap *PMap);
    330 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
    331                                   const NSAPI &NS, Commit &commit);
    332 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
    333                                   const NSAPI &NS, Commit &commit);
    334 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
    335                                             const NSAPI &NS, Commit &commit);
    336 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
    337                                            const NSAPI &NS, Commit &commit);
    338 
    339 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
    340                                       const NSAPI &NS, Commit &commit,
    341                                       const ParentMap *PMap) {
    342   IdentifierInfo *II = nullptr;
    343   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
    344     return false;
    345 
    346   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
    347     return rewriteToArrayLiteral(Msg, NS, commit, PMap);
    348   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
    349     return rewriteToDictionaryLiteral(Msg, NS, commit);
    350   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
    351     return rewriteToNumberLiteral(Msg, NS, commit);
    352   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
    353     return rewriteToStringBoxedExpression(Msg, NS, commit);
    354 
    355   return false;
    356 }
    357 
    358 /// \brief Returns true if the immediate message arguments of \c Msg should not
    359 /// be rewritten because it will interfere with the rewrite of the parent
    360 /// message expression. e.g.
    361 /// \code
    362 ///   [NSDictionary dictionaryWithObjects:
    363 ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
    364 ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
    365 /// \endcode
    366 /// It will return true for this because we are going to rewrite this directly
    367 /// to a dictionary literal without any array literals.
    368 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
    369                                                  const NSAPI &NS);
    370 
    371 //===----------------------------------------------------------------------===//
    372 // rewriteToArrayLiteral.
    373 //===----------------------------------------------------------------------===//
    374 
    375 /// \brief Adds an explicit cast to 'id' if the type is not objc object.
    376 static void objectifyExpr(const Expr *E, Commit &commit);
    377 
    378 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
    379                                   const NSAPI &NS, Commit &commit,
    380                                   const ParentMap *PMap) {
    381   if (PMap) {
    382     const ObjCMessageExpr *ParentMsg =
    383         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
    384     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
    385       return false;
    386   }
    387 
    388   Selector Sel = Msg->getSelector();
    389   SourceRange MsgRange = Msg->getSourceRange();
    390 
    391   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
    392     if (Msg->getNumArgs() != 0)
    393       return false;
    394     commit.replace(MsgRange, "@[]");
    395     return true;
    396   }
    397 
    398   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
    399     if (Msg->getNumArgs() != 1)
    400       return false;
    401     objectifyExpr(Msg->getArg(0), commit);
    402     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
    403     commit.replaceWithInner(MsgRange, ArgRange);
    404     commit.insertWrap("@[", ArgRange, "]");
    405     return true;
    406   }
    407 
    408   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
    409       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
    410     if (Msg->getNumArgs() == 0)
    411       return false;
    412     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
    413     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    414       return false;
    415 
    416     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
    417       objectifyExpr(Msg->getArg(i), commit);
    418 
    419     if (Msg->getNumArgs() == 1) {
    420       commit.replace(MsgRange, "@[]");
    421       return true;
    422     }
    423     SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
    424                          Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
    425     commit.replaceWithInner(MsgRange, ArgRange);
    426     commit.insertWrap("@[", ArgRange, "]");
    427     return true;
    428   }
    429 
    430   return false;
    431 }
    432 
    433 //===----------------------------------------------------------------------===//
    434 // rewriteToDictionaryLiteral.
    435 //===----------------------------------------------------------------------===//
    436 
    437 /// \brief If \c Msg is an NSArray creation message or literal, this gets the
    438 /// objects that were used to create it.
    439 /// \returns true if it is an NSArray and we got objects, or false otherwise.
    440 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
    441                               SmallVectorImpl<const Expr *> &Objs) {
    442   if (!E)
    443     return false;
    444 
    445   E = E->IgnoreParenCasts();
    446   if (!E)
    447     return false;
    448 
    449   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
    450     IdentifierInfo *Cls = nullptr;
    451     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
    452       return false;
    453 
    454     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
    455       return false;
    456 
    457     Selector Sel = Msg->getSelector();
    458     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
    459       return true; // empty array.
    460 
    461     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
    462       if (Msg->getNumArgs() != 1)
    463         return false;
    464       Objs.push_back(Msg->getArg(0));
    465       return true;
    466     }
    467 
    468     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
    469         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
    470       if (Msg->getNumArgs() == 0)
    471         return false;
    472       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
    473       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    474         return false;
    475 
    476       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
    477         Objs.push_back(Msg->getArg(i));
    478       return true;
    479     }
    480 
    481   } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
    482     for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
    483       Objs.push_back(ArrLit->getElement(i));
    484     return true;
    485   }
    486 
    487   return false;
    488 }
    489 
    490 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
    491                                        const NSAPI &NS, Commit &commit) {
    492   Selector Sel = Msg->getSelector();
    493   SourceRange MsgRange = Msg->getSourceRange();
    494 
    495   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
    496     if (Msg->getNumArgs() != 0)
    497       return false;
    498     commit.replace(MsgRange, "@{}");
    499     return true;
    500   }
    501 
    502   if (Sel == NS.getNSDictionarySelector(
    503                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
    504     if (Msg->getNumArgs() != 2)
    505       return false;
    506 
    507     objectifyExpr(Msg->getArg(0), commit);
    508     objectifyExpr(Msg->getArg(1), commit);
    509 
    510     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
    511     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
    512     // Insert key before the value.
    513     commit.insertBefore(ValRange.getBegin(), ": ");
    514     commit.insertFromRange(ValRange.getBegin(),
    515                            CharSourceRange::getTokenRange(KeyRange),
    516                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
    517     commit.insertBefore(ValRange.getBegin(), "@{");
    518     commit.insertAfterToken(ValRange.getEnd(), "}");
    519     commit.replaceWithInner(MsgRange, ValRange);
    520     return true;
    521   }
    522 
    523   if (Sel == NS.getNSDictionarySelector(
    524                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
    525       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
    526     if (Msg->getNumArgs() % 2 != 1)
    527       return false;
    528     unsigned SentinelIdx = Msg->getNumArgs() - 1;
    529     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
    530     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    531       return false;
    532 
    533     if (Msg->getNumArgs() == 1) {
    534       commit.replace(MsgRange, "@{}");
    535       return true;
    536     }
    537 
    538     for (unsigned i = 0; i < SentinelIdx; i += 2) {
    539       objectifyExpr(Msg->getArg(i), commit);
    540       objectifyExpr(Msg->getArg(i+1), commit);
    541 
    542       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
    543       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
    544       // Insert value after key.
    545       commit.insertAfterToken(KeyRange.getEnd(), ": ");
    546       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
    547       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
    548                                                   KeyRange.getBegin()));
    549     }
    550     // Range of arguments up until and including the last key.
    551     // The sentinel and first value are cut off, the value will move after the
    552     // key.
    553     SourceRange ArgRange(Msg->getArg(1)->getLocStart(),
    554                          Msg->getArg(SentinelIdx-1)->getLocEnd());
    555     commit.insertWrap("@{", ArgRange, "}");
    556     commit.replaceWithInner(MsgRange, ArgRange);
    557     return true;
    558   }
    559 
    560   if (Sel == NS.getNSDictionarySelector(
    561                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
    562       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
    563     if (Msg->getNumArgs() != 2)
    564       return false;
    565 
    566     SmallVector<const Expr *, 8> Vals;
    567     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
    568       return false;
    569 
    570     SmallVector<const Expr *, 8> Keys;
    571     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
    572       return false;
    573 
    574     if (Vals.size() != Keys.size())
    575       return false;
    576 
    577     if (Vals.empty()) {
    578       commit.replace(MsgRange, "@{}");
    579       return true;
    580     }
    581 
    582     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
    583       objectifyExpr(Vals[i], commit);
    584       objectifyExpr(Keys[i], commit);
    585 
    586       SourceRange ValRange = Vals[i]->getSourceRange();
    587       SourceRange KeyRange = Keys[i]->getSourceRange();
    588       // Insert value after key.
    589       commit.insertAfterToken(KeyRange.getEnd(), ": ");
    590       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
    591     }
    592     // Range of arguments up until and including the last key.
    593     // The first value is cut off, the value will move after the key.
    594     SourceRange ArgRange(Keys.front()->getLocStart(),
    595                          Keys.back()->getLocEnd());
    596     commit.insertWrap("@{", ArgRange, "}");
    597     commit.replaceWithInner(MsgRange, ArgRange);
    598     return true;
    599   }
    600 
    601   return false;
    602 }
    603 
    604 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
    605                                                  const NSAPI &NS) {
    606   if (!Msg)
    607     return false;
    608 
    609   IdentifierInfo *II = nullptr;
    610   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
    611     return false;
    612 
    613   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
    614     return false;
    615 
    616   Selector Sel = Msg->getSelector();
    617   if (Sel == NS.getNSDictionarySelector(
    618                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
    619       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
    620     if (Msg->getNumArgs() != 2)
    621       return false;
    622 
    623     SmallVector<const Expr *, 8> Vals;
    624     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
    625       return false;
    626 
    627     SmallVector<const Expr *, 8> Keys;
    628     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
    629       return false;
    630 
    631     if (Vals.size() != Keys.size())
    632       return false;
    633 
    634     return true;
    635   }
    636 
    637   return false;
    638 }
    639 
    640 //===----------------------------------------------------------------------===//
    641 // rewriteToNumberLiteral.
    642 //===----------------------------------------------------------------------===//
    643 
    644 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
    645                                    const CharacterLiteral *Arg,
    646                                    const NSAPI &NS, Commit &commit) {
    647   if (Arg->getKind() != CharacterLiteral::Ascii)
    648     return false;
    649   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
    650                                    Msg->getSelector())) {
    651     SourceRange ArgRange = Arg->getSourceRange();
    652     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    653     commit.insert(ArgRange.getBegin(), "@");
    654     return true;
    655   }
    656 
    657   return rewriteToNumericBoxedExpression(Msg, NS, commit);
    658 }
    659 
    660 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
    661                                    const Expr *Arg,
    662                                    const NSAPI &NS, Commit &commit) {
    663   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
    664                                    Msg->getSelector())) {
    665     SourceRange ArgRange = Arg->getSourceRange();
    666     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    667     commit.insert(ArgRange.getBegin(), "@");
    668     return true;
    669   }
    670 
    671   return rewriteToNumericBoxedExpression(Msg, NS, commit);
    672 }
    673 
    674 namespace {
    675 
    676 struct LiteralInfo {
    677   bool Hex, Octal;
    678   StringRef U, F, L, LL;
    679   CharSourceRange WithoutSuffRange;
    680 };
    681 
    682 }
    683 
    684 static bool getLiteralInfo(SourceRange literalRange,
    685                            bool isFloat, bool isIntZero,
    686                           ASTContext &Ctx, LiteralInfo &Info) {
    687   if (literalRange.getBegin().isMacroID() ||
    688       literalRange.getEnd().isMacroID())
    689     return false;
    690   StringRef text = Lexer::getSourceText(
    691                                   CharSourceRange::getTokenRange(literalRange),
    692                                   Ctx.getSourceManager(), Ctx.getLangOpts());
    693   if (text.empty())
    694     return false;
    695 
    696   Optional<bool> UpperU, UpperL;
    697   bool UpperF = false;
    698 
    699   struct Suff {
    700     static bool has(StringRef suff, StringRef &text) {
    701       if (text.endswith(suff)) {
    702         text = text.substr(0, text.size()-suff.size());
    703         return true;
    704       }
    705       return false;
    706     }
    707   };
    708 
    709   while (1) {
    710     if (Suff::has("u", text)) {
    711       UpperU = false;
    712     } else if (Suff::has("U", text)) {
    713       UpperU = true;
    714     } else if (Suff::has("ll", text)) {
    715       UpperL = false;
    716     } else if (Suff::has("LL", text)) {
    717       UpperL = true;
    718     } else if (Suff::has("l", text)) {
    719       UpperL = false;
    720     } else if (Suff::has("L", text)) {
    721       UpperL = true;
    722     } else if (isFloat && Suff::has("f", text)) {
    723       UpperF = false;
    724     } else if (isFloat && Suff::has("F", text)) {
    725       UpperF = true;
    726     } else
    727       break;
    728   }
    729 
    730   if (!UpperU.hasValue() && !UpperL.hasValue())
    731     UpperU = UpperL = true;
    732   else if (UpperU.hasValue() && !UpperL.hasValue())
    733     UpperL = UpperU;
    734   else if (UpperL.hasValue() && !UpperU.hasValue())
    735     UpperU = UpperL;
    736 
    737   Info.U = *UpperU ? "U" : "u";
    738   Info.L = *UpperL ? "L" : "l";
    739   Info.LL = *UpperL ? "LL" : "ll";
    740   Info.F = UpperF ? "F" : "f";
    741 
    742   Info.Hex = Info.Octal = false;
    743   if (text.startswith("0x"))
    744     Info.Hex = true;
    745   else if (!isFloat && !isIntZero && text.startswith("0"))
    746     Info.Octal = true;
    747 
    748   SourceLocation B = literalRange.getBegin();
    749   Info.WithoutSuffRange =
    750       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
    751   return true;
    752 }
    753 
    754 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
    755                                    const NSAPI &NS, Commit &commit) {
    756   if (Msg->getNumArgs() != 1)
    757     return false;
    758 
    759   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
    760   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
    761     return rewriteToCharLiteral(Msg, CharE, NS, commit);
    762   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
    763     return rewriteToBoolLiteral(Msg, BE, NS, commit);
    764   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
    765     return rewriteToBoolLiteral(Msg, BE, NS, commit);
    766 
    767   const Expr *literalE = Arg;
    768   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
    769     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
    770       literalE = UOE->getSubExpr();
    771   }
    772 
    773   // Only integer and floating literals, otherwise try to rewrite to boxed
    774   // expression.
    775   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
    776     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    777 
    778   ASTContext &Ctx = NS.getASTContext();
    779   Selector Sel = Msg->getSelector();
    780   Optional<NSAPI::NSNumberLiteralMethodKind>
    781     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
    782   if (!MKOpt)
    783     return false;
    784   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
    785 
    786   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
    787   bool CallIsFloating = false, CallIsDouble = false;
    788 
    789   switch (MK) {
    790   // We cannot have these calls with int/float literals.
    791   case NSAPI::NSNumberWithChar:
    792   case NSAPI::NSNumberWithUnsignedChar:
    793   case NSAPI::NSNumberWithShort:
    794   case NSAPI::NSNumberWithUnsignedShort:
    795   case NSAPI::NSNumberWithBool:
    796     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    797 
    798   case NSAPI::NSNumberWithUnsignedInt:
    799   case NSAPI::NSNumberWithUnsignedInteger:
    800     CallIsUnsigned = true;
    801   case NSAPI::NSNumberWithInt:
    802   case NSAPI::NSNumberWithInteger:
    803     break;
    804 
    805   case NSAPI::NSNumberWithUnsignedLong:
    806     CallIsUnsigned = true;
    807   case NSAPI::NSNumberWithLong:
    808     CallIsLong = true;
    809     break;
    810 
    811   case NSAPI::NSNumberWithUnsignedLongLong:
    812     CallIsUnsigned = true;
    813   case NSAPI::NSNumberWithLongLong:
    814     CallIsLongLong = true;
    815     break;
    816 
    817   case NSAPI::NSNumberWithDouble:
    818     CallIsDouble = true;
    819   case NSAPI::NSNumberWithFloat:
    820     CallIsFloating = true;
    821     break;
    822   }
    823 
    824   SourceRange ArgRange = Arg->getSourceRange();
    825   QualType ArgTy = Arg->getType();
    826   QualType CallTy = Msg->getArg(0)->getType();
    827 
    828   // Check for the easy case, the literal maps directly to the call.
    829   if (Ctx.hasSameType(ArgTy, CallTy)) {
    830     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    831     commit.insert(ArgRange.getBegin(), "@");
    832     return true;
    833   }
    834 
    835   // We will need to modify the literal suffix to get the same type as the call.
    836   // Try with boxed expression if it came from a macro.
    837   if (ArgRange.getBegin().isMacroID())
    838     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    839 
    840   bool LitIsFloat = ArgTy->isFloatingType();
    841   // For a float passed to integer call, don't try rewriting to objc literal.
    842   // It is difficult and a very uncommon case anyway.
    843   // But try with boxed expression.
    844   if (LitIsFloat && !CallIsFloating)
    845     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    846 
    847   // Try to modify the literal make it the same type as the method call.
    848   // -Modify the suffix, and/or
    849   // -Change integer to float
    850 
    851   LiteralInfo LitInfo;
    852   bool isIntZero = false;
    853   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
    854     isIntZero = !IntE->getValue().getBoolValue();
    855   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
    856     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    857 
    858   // Not easy to do int -> float with hex/octal and uncommon anyway.
    859   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
    860     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    861 
    862   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
    863   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
    864 
    865   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
    866                          LitInfo.WithoutSuffRange);
    867   commit.insert(LitB, "@");
    868 
    869   if (!LitIsFloat && CallIsFloating)
    870     commit.insert(LitE, ".0");
    871 
    872   if (CallIsFloating) {
    873     if (!CallIsDouble)
    874       commit.insert(LitE, LitInfo.F);
    875   } else {
    876     if (CallIsUnsigned)
    877       commit.insert(LitE, LitInfo.U);
    878 
    879     if (CallIsLong)
    880       commit.insert(LitE, LitInfo.L);
    881     else if (CallIsLongLong)
    882       commit.insert(LitE, LitInfo.LL);
    883   }
    884   return true;
    885 }
    886 
    887 // FIXME: Make determination of operator precedence more general and
    888 // make it broadly available.
    889 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
    890   const Expr* Expr = FullExpr->IgnoreImpCasts();
    891   if (isa<ArraySubscriptExpr>(Expr) ||
    892       isa<CallExpr>(Expr) ||
    893       isa<DeclRefExpr>(Expr) ||
    894       isa<CXXNamedCastExpr>(Expr) ||
    895       isa<CXXConstructExpr>(Expr) ||
    896       isa<CXXThisExpr>(Expr) ||
    897       isa<CXXTypeidExpr>(Expr) ||
    898       isa<CXXUnresolvedConstructExpr>(Expr) ||
    899       isa<ObjCMessageExpr>(Expr) ||
    900       isa<ObjCPropertyRefExpr>(Expr) ||
    901       isa<ObjCProtocolExpr>(Expr) ||
    902       isa<MemberExpr>(Expr) ||
    903       isa<ObjCIvarRefExpr>(Expr) ||
    904       isa<ParenExpr>(FullExpr) ||
    905       isa<ParenListExpr>(Expr) ||
    906       isa<SizeOfPackExpr>(Expr))
    907     return false;
    908 
    909   return true;
    910 }
    911 static bool castOperatorNeedsParens(const Expr *FullExpr) {
    912   const Expr* Expr = FullExpr->IgnoreImpCasts();
    913   if (isa<ArraySubscriptExpr>(Expr) ||
    914       isa<CallExpr>(Expr) ||
    915       isa<DeclRefExpr>(Expr) ||
    916       isa<CastExpr>(Expr) ||
    917       isa<CXXNewExpr>(Expr) ||
    918       isa<CXXConstructExpr>(Expr) ||
    919       isa<CXXDeleteExpr>(Expr) ||
    920       isa<CXXNoexceptExpr>(Expr) ||
    921       isa<CXXPseudoDestructorExpr>(Expr) ||
    922       isa<CXXScalarValueInitExpr>(Expr) ||
    923       isa<CXXThisExpr>(Expr) ||
    924       isa<CXXTypeidExpr>(Expr) ||
    925       isa<CXXUnresolvedConstructExpr>(Expr) ||
    926       isa<ObjCMessageExpr>(Expr) ||
    927       isa<ObjCPropertyRefExpr>(Expr) ||
    928       isa<ObjCProtocolExpr>(Expr) ||
    929       isa<MemberExpr>(Expr) ||
    930       isa<ObjCIvarRefExpr>(Expr) ||
    931       isa<ParenExpr>(FullExpr) ||
    932       isa<ParenListExpr>(Expr) ||
    933       isa<SizeOfPackExpr>(Expr) ||
    934       isa<UnaryOperator>(Expr))
    935     return false;
    936 
    937   return true;
    938 }
    939 
    940 static void objectifyExpr(const Expr *E, Commit &commit) {
    941   if (!E) return;
    942 
    943   QualType T = E->getType();
    944   if (T->isObjCObjectPointerType()) {
    945     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
    946       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
    947         return;
    948     } else {
    949       return;
    950     }
    951   } else if (!T->isPointerType()) {
    952     return;
    953   }
    954 
    955   SourceRange Range = E->getSourceRange();
    956   if (castOperatorNeedsParens(E))
    957     commit.insertWrap("(", Range, ")");
    958   commit.insertBefore(Range.getBegin(), "(id)");
    959 }
    960 
    961 //===----------------------------------------------------------------------===//
    962 // rewriteToNumericBoxedExpression.
    963 //===----------------------------------------------------------------------===//
    964 
    965 static bool isEnumConstant(const Expr *E) {
    966   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
    967     if (const ValueDecl *VD = DRE->getDecl())
    968       return isa<EnumConstantDecl>(VD);
    969 
    970   return false;
    971 }
    972 
    973 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
    974                                             const NSAPI &NS, Commit &commit) {
    975   if (Msg->getNumArgs() != 1)
    976     return false;
    977 
    978   const Expr *Arg = Msg->getArg(0);
    979   if (Arg->isTypeDependent())
    980     return false;
    981 
    982   ASTContext &Ctx = NS.getASTContext();
    983   Selector Sel = Msg->getSelector();
    984   Optional<NSAPI::NSNumberLiteralMethodKind>
    985     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
    986   if (!MKOpt)
    987     return false;
    988   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
    989 
    990   const Expr *OrigArg = Arg->IgnoreImpCasts();
    991   QualType FinalTy = Arg->getType();
    992   QualType OrigTy = OrigArg->getType();
    993   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
    994   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
    995 
    996   bool isTruncated = FinalTySize < OrigTySize;
    997   bool needsCast = false;
    998 
    999   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
   1000     switch (ICE->getCastKind()) {
   1001     case CK_LValueToRValue:
   1002     case CK_NoOp:
   1003     case CK_UserDefinedConversion:
   1004       break;
   1005 
   1006     case CK_IntegralCast: {
   1007       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
   1008         break;
   1009       // Be more liberal with Integer/UnsignedInteger which are very commonly
   1010       // used.
   1011       if ((MK == NSAPI::NSNumberWithInteger ||
   1012            MK == NSAPI::NSNumberWithUnsignedInteger) &&
   1013           !isTruncated) {
   1014         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
   1015           break;
   1016         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
   1017             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
   1018           break;
   1019       }
   1020 
   1021       needsCast = true;
   1022       break;
   1023     }
   1024 
   1025     case CK_PointerToBoolean:
   1026     case CK_IntegralToBoolean:
   1027     case CK_IntegralToFloating:
   1028     case CK_FloatingToIntegral:
   1029     case CK_FloatingToBoolean:
   1030     case CK_FloatingCast:
   1031     case CK_FloatingComplexToReal:
   1032     case CK_FloatingComplexToBoolean:
   1033     case CK_IntegralComplexToReal:
   1034     case CK_IntegralComplexToBoolean:
   1035     case CK_AtomicToNonAtomic:
   1036     case CK_AddressSpaceConversion:
   1037       needsCast = true;
   1038       break;
   1039 
   1040     case CK_Dependent:
   1041     case CK_BitCast:
   1042     case CK_LValueBitCast:
   1043     case CK_BaseToDerived:
   1044     case CK_DerivedToBase:
   1045     case CK_UncheckedDerivedToBase:
   1046     case CK_Dynamic:
   1047     case CK_ToUnion:
   1048     case CK_ArrayToPointerDecay:
   1049     case CK_FunctionToPointerDecay:
   1050     case CK_NullToPointer:
   1051     case CK_NullToMemberPointer:
   1052     case CK_BaseToDerivedMemberPointer:
   1053     case CK_DerivedToBaseMemberPointer:
   1054     case CK_MemberPointerToBoolean:
   1055     case CK_ReinterpretMemberPointer:
   1056     case CK_ConstructorConversion:
   1057     case CK_IntegralToPointer:
   1058     case CK_PointerToIntegral:
   1059     case CK_ToVoid:
   1060     case CK_VectorSplat:
   1061     case CK_CPointerToObjCPointerCast:
   1062     case CK_BlockPointerToObjCPointerCast:
   1063     case CK_AnyPointerToBlockPointerCast:
   1064     case CK_ObjCObjectLValueCast:
   1065     case CK_FloatingRealToComplex:
   1066     case CK_FloatingComplexCast:
   1067     case CK_FloatingComplexToIntegralComplex:
   1068     case CK_IntegralRealToComplex:
   1069     case CK_IntegralComplexCast:
   1070     case CK_IntegralComplexToFloatingComplex:
   1071     case CK_ARCProduceObject:
   1072     case CK_ARCConsumeObject:
   1073     case CK_ARCReclaimReturnedObject:
   1074     case CK_ARCExtendBlockObject:
   1075     case CK_NonAtomicToAtomic:
   1076     case CK_CopyAndAutoreleaseBlockObject:
   1077     case CK_BuiltinFnToFnPtr:
   1078     case CK_ZeroToOCLEvent:
   1079       return false;
   1080 
   1081     case CK_BooleanToSignedIntegral:
   1082       llvm_unreachable("OpenCL-specific cast in Objective-C?");
   1083     }
   1084   }
   1085 
   1086   if (needsCast) {
   1087     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
   1088     // FIXME: Use a custom category name to distinguish migration diagnostics.
   1089     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
   1090                        "converting to boxing syntax requires casting %0 to %1");
   1091     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
   1092         << Msg->getSourceRange();
   1093     return false;
   1094   }
   1095 
   1096   SourceRange ArgRange = OrigArg->getSourceRange();
   1097   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
   1098 
   1099   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
   1100     commit.insertBefore(ArgRange.getBegin(), "@");
   1101   else
   1102     commit.insertWrap("@(", ArgRange, ")");
   1103 
   1104   return true;
   1105 }
   1106 
   1107 //===----------------------------------------------------------------------===//
   1108 // rewriteToStringBoxedExpression.
   1109 //===----------------------------------------------------------------------===//
   1110 
   1111 static bool doRewriteToUTF8StringBoxedExpressionHelper(
   1112                                               const ObjCMessageExpr *Msg,
   1113                                               const NSAPI &NS, Commit &commit) {
   1114   const Expr *Arg = Msg->getArg(0);
   1115   if (Arg->isTypeDependent())
   1116     return false;
   1117 
   1118   ASTContext &Ctx = NS.getASTContext();
   1119 
   1120   const Expr *OrigArg = Arg->IgnoreImpCasts();
   1121   QualType OrigTy = OrigArg->getType();
   1122   if (OrigTy->isArrayType())
   1123     OrigTy = Ctx.getArrayDecayedType(OrigTy);
   1124 
   1125   if (const StringLiteral *
   1126         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
   1127     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
   1128     commit.insert(StrE->getLocStart(), "@");
   1129     return true;
   1130   }
   1131 
   1132   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
   1133     QualType PointeeType = PT->getPointeeType();
   1134     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
   1135       SourceRange ArgRange = OrigArg->getSourceRange();
   1136       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
   1137 
   1138       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
   1139         commit.insertBefore(ArgRange.getBegin(), "@");
   1140       else
   1141         commit.insertWrap("@(", ArgRange, ")");
   1142 
   1143       return true;
   1144     }
   1145   }
   1146 
   1147   return false;
   1148 }
   1149 
   1150 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
   1151                                            const NSAPI &NS, Commit &commit) {
   1152   Selector Sel = Msg->getSelector();
   1153 
   1154   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
   1155       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
   1156       Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
   1157     if (Msg->getNumArgs() != 1)
   1158       return false;
   1159     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
   1160   }
   1161 
   1162   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
   1163     if (Msg->getNumArgs() != 2)
   1164       return false;
   1165 
   1166     const Expr *encodingArg = Msg->getArg(1);
   1167     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
   1168         NS.isNSASCIIStringEncodingConstant(encodingArg))
   1169       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
   1170   }
   1171 
   1172   return false;
   1173 }
   1174