Home | History | Annotate | Line # | Download | only in Edit
      1 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // Rewrites legacy method calls to modern syntax.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "clang/Edit/Rewriters.h"
     14 #include "clang/AST/ASTContext.h"
     15 #include "clang/AST/ExprCXX.h"
     16 #include "clang/AST/ExprObjC.h"
     17 #include "clang/AST/NSAPI.h"
     18 #include "clang/AST/ParentMap.h"
     19 #include "clang/Edit/Commit.h"
     20 #include "clang/Lex/Lexer.h"
     21 
     22 using namespace clang;
     23 using namespace edit;
     24 
     25 static 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 
     58 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
     59                                               const NSAPI &NS, Commit &commit) {
     60   IdentifierInfo *II = nullptr;
     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 /// 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'.
    107 static const ObjCInterfaceDecl *
    108 maybeAdjustInterfaceForSubscriptingCheck(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 
    155 static 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 
    171 static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
    172 
    173 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
    174   if (subscriptOperatorNeedsParens(Receiver)) {
    175     SourceRange RecRange = Receiver->getSourceRange();
    176     commit.insertWrap("(", RecRange, ")");
    177   }
    178 }
    179 
    180 static 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 
    202 static 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 
    212 static 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 
    222 static 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 
    256 static 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 
    289 bool 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 *IFace =
    299       NS.getASTContext().getObjContainingInterface(Method);
    300   if (!IFace)
    301     return false;
    302   Selector Sel = Msg->getSelector();
    303 
    304   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
    305     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
    306 
    307   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
    308     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
    309 
    310   if (Msg->getNumArgs() != 2)
    311     return false;
    312 
    313   if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
    314     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
    315 
    316   if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
    317     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
    318 
    319   return false;
    320 }
    321 
    322 //===----------------------------------------------------------------------===//
    323 // rewriteToObjCLiteralSyntax.
    324 //===----------------------------------------------------------------------===//
    325 
    326 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
    327                                   const NSAPI &NS, Commit &commit,
    328                                   const ParentMap *PMap);
    329 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
    330                                   const NSAPI &NS, Commit &commit);
    331 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
    332                                   const NSAPI &NS, Commit &commit);
    333 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
    334                                             const NSAPI &NS, Commit &commit);
    335 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
    336                                            const NSAPI &NS, Commit &commit);
    337 
    338 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
    339                                       const NSAPI &NS, Commit &commit,
    340                                       const ParentMap *PMap) {
    341   IdentifierInfo *II = nullptr;
    342   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
    343     return false;
    344 
    345   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
    346     return rewriteToArrayLiteral(Msg, NS, commit, PMap);
    347   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
    348     return rewriteToDictionaryLiteral(Msg, NS, commit);
    349   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
    350     return rewriteToNumberLiteral(Msg, NS, commit);
    351   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
    352     return rewriteToStringBoxedExpression(Msg, NS, commit);
    353 
    354   return false;
    355 }
    356 
    357 /// Returns true if the immediate message arguments of \c Msg should not
    358 /// be rewritten because it will interfere with the rewrite of the parent
    359 /// message expression. e.g.
    360 /// \code
    361 ///   [NSDictionary dictionaryWithObjects:
    362 ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
    363 ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
    364 /// \endcode
    365 /// It will return true for this because we are going to rewrite this directly
    366 /// to a dictionary literal without any array literals.
    367 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
    368                                                  const NSAPI &NS);
    369 
    370 //===----------------------------------------------------------------------===//
    371 // rewriteToArrayLiteral.
    372 //===----------------------------------------------------------------------===//
    373 
    374 /// Adds an explicit cast to 'id' if the type is not objc object.
    375 static void objectifyExpr(const Expr *E, Commit &commit);
    376 
    377 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
    378                                   const NSAPI &NS, Commit &commit,
    379                                   const ParentMap *PMap) {
    380   if (PMap) {
    381     const ObjCMessageExpr *ParentMsg =
    382         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
    383     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
    384       return false;
    385   }
    386 
    387   Selector Sel = Msg->getSelector();
    388   SourceRange MsgRange = Msg->getSourceRange();
    389 
    390   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
    391     if (Msg->getNumArgs() != 0)
    392       return false;
    393     commit.replace(MsgRange, "@[]");
    394     return true;
    395   }
    396 
    397   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
    398     if (Msg->getNumArgs() != 1)
    399       return false;
    400     objectifyExpr(Msg->getArg(0), commit);
    401     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
    402     commit.replaceWithInner(MsgRange, ArgRange);
    403     commit.insertWrap("@[", ArgRange, "]");
    404     return true;
    405   }
    406 
    407   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
    408       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
    409     if (Msg->getNumArgs() == 0)
    410       return false;
    411     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
    412     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    413       return false;
    414 
    415     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
    416       objectifyExpr(Msg->getArg(i), commit);
    417 
    418     if (Msg->getNumArgs() == 1) {
    419       commit.replace(MsgRange, "@[]");
    420       return true;
    421     }
    422     SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
    423                          Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
    424     commit.replaceWithInner(MsgRange, ArgRange);
    425     commit.insertWrap("@[", ArgRange, "]");
    426     return true;
    427   }
    428 
    429   return false;
    430 }
    431 
    432 //===----------------------------------------------------------------------===//
    433 // rewriteToDictionaryLiteral.
    434 //===----------------------------------------------------------------------===//
    435 
    436 /// If \c Msg is an NSArray creation message or literal, this gets the
    437 /// objects that were used to create it.
    438 /// \returns true if it is an NSArray and we got objects, or false otherwise.
    439 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
    440                               SmallVectorImpl<const Expr *> &Objs) {
    441   if (!E)
    442     return false;
    443 
    444   E = E->IgnoreParenCasts();
    445   if (!E)
    446     return false;
    447 
    448   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
    449     IdentifierInfo *Cls = nullptr;
    450     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
    451       return false;
    452 
    453     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
    454       return false;
    455 
    456     Selector Sel = Msg->getSelector();
    457     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
    458       return true; // empty array.
    459 
    460     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
    461       if (Msg->getNumArgs() != 1)
    462         return false;
    463       Objs.push_back(Msg->getArg(0));
    464       return true;
    465     }
    466 
    467     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
    468         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
    469       if (Msg->getNumArgs() == 0)
    470         return false;
    471       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
    472       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    473         return false;
    474 
    475       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
    476         Objs.push_back(Msg->getArg(i));
    477       return true;
    478     }
    479 
    480   } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
    481     for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
    482       Objs.push_back(ArrLit->getElement(i));
    483     return true;
    484   }
    485 
    486   return false;
    487 }
    488 
    489 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
    490                                        const NSAPI &NS, Commit &commit) {
    491   Selector Sel = Msg->getSelector();
    492   SourceRange MsgRange = Msg->getSourceRange();
    493 
    494   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
    495     if (Msg->getNumArgs() != 0)
    496       return false;
    497     commit.replace(MsgRange, "@{}");
    498     return true;
    499   }
    500 
    501   if (Sel == NS.getNSDictionarySelector(
    502                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
    503     if (Msg->getNumArgs() != 2)
    504       return false;
    505 
    506     objectifyExpr(Msg->getArg(0), commit);
    507     objectifyExpr(Msg->getArg(1), commit);
    508 
    509     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
    510     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
    511     // Insert key before the value.
    512     commit.insertBefore(ValRange.getBegin(), ": ");
    513     commit.insertFromRange(ValRange.getBegin(),
    514                            CharSourceRange::getTokenRange(KeyRange),
    515                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
    516     commit.insertBefore(ValRange.getBegin(), "@{");
    517     commit.insertAfterToken(ValRange.getEnd(), "}");
    518     commit.replaceWithInner(MsgRange, ValRange);
    519     return true;
    520   }
    521 
    522   if (Sel == NS.getNSDictionarySelector(
    523                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
    524       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
    525     if (Msg->getNumArgs() % 2 != 1)
    526       return false;
    527     unsigned SentinelIdx = Msg->getNumArgs() - 1;
    528     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
    529     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
    530       return false;
    531 
    532     if (Msg->getNumArgs() == 1) {
    533       commit.replace(MsgRange, "@{}");
    534       return true;
    535     }
    536 
    537     for (unsigned i = 0; i < SentinelIdx; i += 2) {
    538       objectifyExpr(Msg->getArg(i), commit);
    539       objectifyExpr(Msg->getArg(i+1), commit);
    540 
    541       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
    542       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
    543       // Insert value after key.
    544       commit.insertAfterToken(KeyRange.getEnd(), ": ");
    545       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
    546       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
    547                                                   KeyRange.getBegin()));
    548     }
    549     // Range of arguments up until and including the last key.
    550     // The sentinel and first value are cut off, the value will move after the
    551     // key.
    552     SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
    553                          Msg->getArg(SentinelIdx - 1)->getEndLoc());
    554     commit.insertWrap("@{", ArgRange, "}");
    555     commit.replaceWithInner(MsgRange, ArgRange);
    556     return true;
    557   }
    558 
    559   if (Sel == NS.getNSDictionarySelector(
    560                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
    561       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
    562     if (Msg->getNumArgs() != 2)
    563       return false;
    564 
    565     SmallVector<const Expr *, 8> Vals;
    566     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
    567       return false;
    568 
    569     SmallVector<const Expr *, 8> Keys;
    570     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
    571       return false;
    572 
    573     if (Vals.size() != Keys.size())
    574       return false;
    575 
    576     if (Vals.empty()) {
    577       commit.replace(MsgRange, "@{}");
    578       return true;
    579     }
    580 
    581     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
    582       objectifyExpr(Vals[i], commit);
    583       objectifyExpr(Keys[i], commit);
    584 
    585       SourceRange ValRange = Vals[i]->getSourceRange();
    586       SourceRange KeyRange = Keys[i]->getSourceRange();
    587       // Insert value after key.
    588       commit.insertAfterToken(KeyRange.getEnd(), ": ");
    589       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
    590     }
    591     // Range of arguments up until and including the last key.
    592     // The first value is cut off, the value will move after the key.
    593     SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());
    594     commit.insertWrap("@{", ArgRange, "}");
    595     commit.replaceWithInner(MsgRange, ArgRange);
    596     return true;
    597   }
    598 
    599   return false;
    600 }
    601 
    602 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
    603                                                  const NSAPI &NS) {
    604   if (!Msg)
    605     return false;
    606 
    607   IdentifierInfo *II = nullptr;
    608   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
    609     return false;
    610 
    611   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
    612     return false;
    613 
    614   Selector Sel = Msg->getSelector();
    615   if (Sel == NS.getNSDictionarySelector(
    616                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
    617       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
    618     if (Msg->getNumArgs() != 2)
    619       return false;
    620 
    621     SmallVector<const Expr *, 8> Vals;
    622     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
    623       return false;
    624 
    625     SmallVector<const Expr *, 8> Keys;
    626     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
    627       return false;
    628 
    629     if (Vals.size() != Keys.size())
    630       return false;
    631 
    632     return true;
    633   }
    634 
    635   return false;
    636 }
    637 
    638 //===----------------------------------------------------------------------===//
    639 // rewriteToNumberLiteral.
    640 //===----------------------------------------------------------------------===//
    641 
    642 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
    643                                    const CharacterLiteral *Arg,
    644                                    const NSAPI &NS, Commit &commit) {
    645   if (Arg->getKind() != CharacterLiteral::Ascii)
    646     return false;
    647   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
    648                                    Msg->getSelector())) {
    649     SourceRange ArgRange = Arg->getSourceRange();
    650     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    651     commit.insert(ArgRange.getBegin(), "@");
    652     return true;
    653   }
    654 
    655   return rewriteToNumericBoxedExpression(Msg, NS, commit);
    656 }
    657 
    658 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
    659                                    const Expr *Arg,
    660                                    const NSAPI &NS, Commit &commit) {
    661   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
    662                                    Msg->getSelector())) {
    663     SourceRange ArgRange = Arg->getSourceRange();
    664     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    665     commit.insert(ArgRange.getBegin(), "@");
    666     return true;
    667   }
    668 
    669   return rewriteToNumericBoxedExpression(Msg, NS, commit);
    670 }
    671 
    672 namespace {
    673 
    674 struct LiteralInfo {
    675   bool Hex, Octal;
    676   StringRef U, F, L, LL;
    677   CharSourceRange WithoutSuffRange;
    678 };
    679 
    680 }
    681 
    682 static bool getLiteralInfo(SourceRange literalRange,
    683                            bool isFloat, bool isIntZero,
    684                           ASTContext &Ctx, LiteralInfo &Info) {
    685   if (literalRange.getBegin().isMacroID() ||
    686       literalRange.getEnd().isMacroID())
    687     return false;
    688   StringRef text = Lexer::getSourceText(
    689                                   CharSourceRange::getTokenRange(literalRange),
    690                                   Ctx.getSourceManager(), Ctx.getLangOpts());
    691   if (text.empty())
    692     return false;
    693 
    694   Optional<bool> UpperU, UpperL;
    695   bool UpperF = false;
    696 
    697   struct Suff {
    698     static bool has(StringRef suff, StringRef &text) {
    699       if (text.endswith(suff)) {
    700         text = text.substr(0, text.size()-suff.size());
    701         return true;
    702       }
    703       return false;
    704     }
    705   };
    706 
    707   while (1) {
    708     if (Suff::has("u", text)) {
    709       UpperU = false;
    710     } else if (Suff::has("U", text)) {
    711       UpperU = true;
    712     } else if (Suff::has("ll", text)) {
    713       UpperL = false;
    714     } else if (Suff::has("LL", text)) {
    715       UpperL = true;
    716     } else if (Suff::has("l", text)) {
    717       UpperL = false;
    718     } else if (Suff::has("L", text)) {
    719       UpperL = true;
    720     } else if (isFloat && Suff::has("f", text)) {
    721       UpperF = false;
    722     } else if (isFloat && Suff::has("F", text)) {
    723       UpperF = true;
    724     } else
    725       break;
    726   }
    727 
    728   if (!UpperU.hasValue() && !UpperL.hasValue())
    729     UpperU = UpperL = true;
    730   else if (UpperU.hasValue() && !UpperL.hasValue())
    731     UpperL = UpperU;
    732   else if (UpperL.hasValue() && !UpperU.hasValue())
    733     UpperU = UpperL;
    734 
    735   Info.U = *UpperU ? "U" : "u";
    736   Info.L = *UpperL ? "L" : "l";
    737   Info.LL = *UpperL ? "LL" : "ll";
    738   Info.F = UpperF ? "F" : "f";
    739 
    740   Info.Hex = Info.Octal = false;
    741   if (text.startswith("0x"))
    742     Info.Hex = true;
    743   else if (!isFloat && !isIntZero && text.startswith("0"))
    744     Info.Octal = true;
    745 
    746   SourceLocation B = literalRange.getBegin();
    747   Info.WithoutSuffRange =
    748       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
    749   return true;
    750 }
    751 
    752 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
    753                                    const NSAPI &NS, Commit &commit) {
    754   if (Msg->getNumArgs() != 1)
    755     return false;
    756 
    757   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
    758   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
    759     return rewriteToCharLiteral(Msg, CharE, NS, commit);
    760   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
    761     return rewriteToBoolLiteral(Msg, BE, NS, commit);
    762   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
    763     return rewriteToBoolLiteral(Msg, BE, NS, commit);
    764 
    765   const Expr *literalE = Arg;
    766   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
    767     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
    768       literalE = UOE->getSubExpr();
    769   }
    770 
    771   // Only integer and floating literals, otherwise try to rewrite to boxed
    772   // expression.
    773   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
    774     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    775 
    776   ASTContext &Ctx = NS.getASTContext();
    777   Selector Sel = Msg->getSelector();
    778   Optional<NSAPI::NSNumberLiteralMethodKind>
    779     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
    780   if (!MKOpt)
    781     return false;
    782   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
    783 
    784   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
    785   bool CallIsFloating = false, CallIsDouble = false;
    786 
    787   switch (MK) {
    788   // We cannot have these calls with int/float literals.
    789   case NSAPI::NSNumberWithChar:
    790   case NSAPI::NSNumberWithUnsignedChar:
    791   case NSAPI::NSNumberWithShort:
    792   case NSAPI::NSNumberWithUnsignedShort:
    793   case NSAPI::NSNumberWithBool:
    794     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    795 
    796   case NSAPI::NSNumberWithUnsignedInt:
    797   case NSAPI::NSNumberWithUnsignedInteger:
    798     CallIsUnsigned = true;
    799     LLVM_FALLTHROUGH;
    800   case NSAPI::NSNumberWithInt:
    801   case NSAPI::NSNumberWithInteger:
    802     break;
    803 
    804   case NSAPI::NSNumberWithUnsignedLong:
    805     CallIsUnsigned = true;
    806     LLVM_FALLTHROUGH;
    807   case NSAPI::NSNumberWithLong:
    808     CallIsLong = true;
    809     break;
    810 
    811   case NSAPI::NSNumberWithUnsignedLongLong:
    812     CallIsUnsigned = true;
    813     LLVM_FALLTHROUGH;
    814   case NSAPI::NSNumberWithLongLong:
    815     CallIsLongLong = true;
    816     break;
    817 
    818   case NSAPI::NSNumberWithDouble:
    819     CallIsDouble = true;
    820     LLVM_FALLTHROUGH;
    821   case NSAPI::NSNumberWithFloat:
    822     CallIsFloating = true;
    823     break;
    824   }
    825 
    826   SourceRange ArgRange = Arg->getSourceRange();
    827   QualType ArgTy = Arg->getType();
    828   QualType CallTy = Msg->getArg(0)->getType();
    829 
    830   // Check for the easy case, the literal maps directly to the call.
    831   if (Ctx.hasSameType(ArgTy, CallTy)) {
    832     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
    833     commit.insert(ArgRange.getBegin(), "@");
    834     return true;
    835   }
    836 
    837   // We will need to modify the literal suffix to get the same type as the call.
    838   // Try with boxed expression if it came from a macro.
    839   if (ArgRange.getBegin().isMacroID())
    840     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    841 
    842   bool LitIsFloat = ArgTy->isFloatingType();
    843   // For a float passed to integer call, don't try rewriting to objc literal.
    844   // It is difficult and a very uncommon case anyway.
    845   // But try with boxed expression.
    846   if (LitIsFloat && !CallIsFloating)
    847     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    848 
    849   // Try to modify the literal make it the same type as the method call.
    850   // -Modify the suffix, and/or
    851   // -Change integer to float
    852 
    853   LiteralInfo LitInfo;
    854   bool isIntZero = false;
    855   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
    856     isIntZero = !IntE->getValue().getBoolValue();
    857   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
    858     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    859 
    860   // Not easy to do int -> float with hex/octal and uncommon anyway.
    861   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
    862     return rewriteToNumericBoxedExpression(Msg, NS, commit);
    863 
    864   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
    865   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
    866 
    867   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
    868                          LitInfo.WithoutSuffRange);
    869   commit.insert(LitB, "@");
    870 
    871   if (!LitIsFloat && CallIsFloating)
    872     commit.insert(LitE, ".0");
    873 
    874   if (CallIsFloating) {
    875     if (!CallIsDouble)
    876       commit.insert(LitE, LitInfo.F);
    877   } else {
    878     if (CallIsUnsigned)
    879       commit.insert(LitE, LitInfo.U);
    880 
    881     if (CallIsLong)
    882       commit.insert(LitE, LitInfo.L);
    883     else if (CallIsLongLong)
    884       commit.insert(LitE, LitInfo.LL);
    885   }
    886   return true;
    887 }
    888 
    889 // FIXME: Make determination of operator precedence more general and
    890 // make it broadly available.
    891 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
    892   const Expr* Expr = FullExpr->IgnoreImpCasts();
    893   if (isa<ArraySubscriptExpr>(Expr) ||
    894       isa<CallExpr>(Expr) ||
    895       isa<DeclRefExpr>(Expr) ||
    896       isa<CXXNamedCastExpr>(Expr) ||
    897       isa<CXXConstructExpr>(Expr) ||
    898       isa<CXXThisExpr>(Expr) ||
    899       isa<CXXTypeidExpr>(Expr) ||
    900       isa<CXXUnresolvedConstructExpr>(Expr) ||
    901       isa<ObjCMessageExpr>(Expr) ||
    902       isa<ObjCPropertyRefExpr>(Expr) ||
    903       isa<ObjCProtocolExpr>(Expr) ||
    904       isa<MemberExpr>(Expr) ||
    905       isa<ObjCIvarRefExpr>(Expr) ||
    906       isa<ParenExpr>(FullExpr) ||
    907       isa<ParenListExpr>(Expr) ||
    908       isa<SizeOfPackExpr>(Expr))
    909     return false;
    910 
    911   return true;
    912 }
    913 static bool castOperatorNeedsParens(const Expr *FullExpr) {
    914   const Expr* Expr = FullExpr->IgnoreImpCasts();
    915   if (isa<ArraySubscriptExpr>(Expr) ||
    916       isa<CallExpr>(Expr) ||
    917       isa<DeclRefExpr>(Expr) ||
    918       isa<CastExpr>(Expr) ||
    919       isa<CXXNewExpr>(Expr) ||
    920       isa<CXXConstructExpr>(Expr) ||
    921       isa<CXXDeleteExpr>(Expr) ||
    922       isa<CXXNoexceptExpr>(Expr) ||
    923       isa<CXXPseudoDestructorExpr>(Expr) ||
    924       isa<CXXScalarValueInitExpr>(Expr) ||
    925       isa<CXXThisExpr>(Expr) ||
    926       isa<CXXTypeidExpr>(Expr) ||
    927       isa<CXXUnresolvedConstructExpr>(Expr) ||
    928       isa<ObjCMessageExpr>(Expr) ||
    929       isa<ObjCPropertyRefExpr>(Expr) ||
    930       isa<ObjCProtocolExpr>(Expr) ||
    931       isa<MemberExpr>(Expr) ||
    932       isa<ObjCIvarRefExpr>(Expr) ||
    933       isa<ParenExpr>(FullExpr) ||
    934       isa<ParenListExpr>(Expr) ||
    935       isa<SizeOfPackExpr>(Expr) ||
    936       isa<UnaryOperator>(Expr))
    937     return false;
    938 
    939   return true;
    940 }
    941 
    942 static void objectifyExpr(const Expr *E, Commit &commit) {
    943   if (!E) return;
    944 
    945   QualType T = E->getType();
    946   if (T->isObjCObjectPointerType()) {
    947     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
    948       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
    949         return;
    950     } else {
    951       return;
    952     }
    953   } else if (!T->isPointerType()) {
    954     return;
    955   }
    956 
    957   SourceRange Range = E->getSourceRange();
    958   if (castOperatorNeedsParens(E))
    959     commit.insertWrap("(", Range, ")");
    960   commit.insertBefore(Range.getBegin(), "(id)");
    961 }
    962 
    963 //===----------------------------------------------------------------------===//
    964 // rewriteToNumericBoxedExpression.
    965 //===----------------------------------------------------------------------===//
    966 
    967 static bool isEnumConstant(const Expr *E) {
    968   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
    969     if (const ValueDecl *VD = DRE->getDecl())
    970       return isa<EnumConstantDecl>(VD);
    971 
    972   return false;
    973 }
    974 
    975 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
    976                                             const NSAPI &NS, Commit &commit) {
    977   if (Msg->getNumArgs() != 1)
    978     return false;
    979 
    980   const Expr *Arg = Msg->getArg(0);
    981   if (Arg->isTypeDependent())
    982     return false;
    983 
    984   ASTContext &Ctx = NS.getASTContext();
    985   Selector Sel = Msg->getSelector();
    986   Optional<NSAPI::NSNumberLiteralMethodKind>
    987     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
    988   if (!MKOpt)
    989     return false;
    990   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
    991 
    992   const Expr *OrigArg = Arg->IgnoreImpCasts();
    993   QualType FinalTy = Arg->getType();
    994   QualType OrigTy = OrigArg->getType();
    995   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
    996   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
    997 
    998   bool isTruncated = FinalTySize < OrigTySize;
    999   bool needsCast = false;
   1000 
   1001   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
   1002     switch (ICE->getCastKind()) {
   1003     case CK_LValueToRValue:
   1004     case CK_NoOp:
   1005     case CK_UserDefinedConversion:
   1006       break;
   1007 
   1008     case CK_IntegralCast: {
   1009       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
   1010         break;
   1011       // Be more liberal with Integer/UnsignedInteger which are very commonly
   1012       // used.
   1013       if ((MK == NSAPI::NSNumberWithInteger ||
   1014            MK == NSAPI::NSNumberWithUnsignedInteger) &&
   1015           !isTruncated) {
   1016         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
   1017           break;
   1018         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
   1019             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
   1020           break;
   1021       }
   1022 
   1023       needsCast = true;
   1024       break;
   1025     }
   1026 
   1027     case CK_PointerToBoolean:
   1028     case CK_IntegralToBoolean:
   1029     case CK_IntegralToFloating:
   1030     case CK_FloatingToIntegral:
   1031     case CK_FloatingToBoolean:
   1032     case CK_FloatingCast:
   1033     case CK_FloatingComplexToReal:
   1034     case CK_FloatingComplexToBoolean:
   1035     case CK_IntegralComplexToReal:
   1036     case CK_IntegralComplexToBoolean:
   1037     case CK_AtomicToNonAtomic:
   1038     case CK_AddressSpaceConversion:
   1039       needsCast = true;
   1040       break;
   1041 
   1042     case CK_Dependent:
   1043     case CK_BitCast:
   1044     case CK_LValueBitCast:
   1045     case CK_LValueToRValueBitCast:
   1046     case CK_BaseToDerived:
   1047     case CK_DerivedToBase:
   1048     case CK_UncheckedDerivedToBase:
   1049     case CK_Dynamic:
   1050     case CK_ToUnion:
   1051     case CK_ArrayToPointerDecay:
   1052     case CK_FunctionToPointerDecay:
   1053     case CK_NullToPointer:
   1054     case CK_NullToMemberPointer:
   1055     case CK_BaseToDerivedMemberPointer:
   1056     case CK_DerivedToBaseMemberPointer:
   1057     case CK_MemberPointerToBoolean:
   1058     case CK_ReinterpretMemberPointer:
   1059     case CK_ConstructorConversion:
   1060     case CK_IntegralToPointer:
   1061     case CK_PointerToIntegral:
   1062     case CK_ToVoid:
   1063     case CK_VectorSplat:
   1064     case CK_CPointerToObjCPointerCast:
   1065     case CK_BlockPointerToObjCPointerCast:
   1066     case CK_AnyPointerToBlockPointerCast:
   1067     case CK_ObjCObjectLValueCast:
   1068     case CK_FloatingRealToComplex:
   1069     case CK_FloatingComplexCast:
   1070     case CK_FloatingComplexToIntegralComplex:
   1071     case CK_IntegralRealToComplex:
   1072     case CK_IntegralComplexCast:
   1073     case CK_IntegralComplexToFloatingComplex:
   1074     case CK_ARCProduceObject:
   1075     case CK_ARCConsumeObject:
   1076     case CK_ARCReclaimReturnedObject:
   1077     case CK_ARCExtendBlockObject:
   1078     case CK_NonAtomicToAtomic:
   1079     case CK_CopyAndAutoreleaseBlockObject:
   1080     case CK_BuiltinFnToFnPtr:
   1081     case CK_ZeroToOCLOpaqueType:
   1082     case CK_IntToOCLSampler:
   1083     case CK_MatrixCast:
   1084       return false;
   1085 
   1086     case CK_BooleanToSignedIntegral:
   1087       llvm_unreachable("OpenCL-specific cast in Objective-C?");
   1088 
   1089     case CK_FloatingToFixedPoint:
   1090     case CK_FixedPointToFloating:
   1091     case CK_FixedPointCast:
   1092     case CK_FixedPointToBoolean:
   1093     case CK_FixedPointToIntegral:
   1094     case CK_IntegralToFixedPoint:
   1095       llvm_unreachable("Fixed point types are disabled for Objective-C");
   1096     }
   1097   }
   1098 
   1099   if (needsCast) {
   1100     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
   1101     // FIXME: Use a custom category name to distinguish migration diagnostics.
   1102     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
   1103                        "converting to boxing syntax requires casting %0 to %1");
   1104     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
   1105         << Msg->getSourceRange();
   1106     return false;
   1107   }
   1108 
   1109   SourceRange ArgRange = OrigArg->getSourceRange();
   1110   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
   1111 
   1112   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
   1113     commit.insertBefore(ArgRange.getBegin(), "@");
   1114   else
   1115     commit.insertWrap("@(", ArgRange, ")");
   1116 
   1117   return true;
   1118 }
   1119 
   1120 //===----------------------------------------------------------------------===//
   1121 // rewriteToStringBoxedExpression.
   1122 //===----------------------------------------------------------------------===//
   1123 
   1124 static bool doRewriteToUTF8StringBoxedExpressionHelper(
   1125                                               const ObjCMessageExpr *Msg,
   1126                                               const NSAPI &NS, Commit &commit) {
   1127   const Expr *Arg = Msg->getArg(0);
   1128   if (Arg->isTypeDependent())
   1129     return false;
   1130 
   1131   ASTContext &Ctx = NS.getASTContext();
   1132 
   1133   const Expr *OrigArg = Arg->IgnoreImpCasts();
   1134   QualType OrigTy = OrigArg->getType();
   1135   if (OrigTy->isArrayType())
   1136     OrigTy = Ctx.getArrayDecayedType(OrigTy);
   1137 
   1138   if (const StringLiteral *
   1139         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
   1140     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
   1141     commit.insert(StrE->getBeginLoc(), "@");
   1142     return true;
   1143   }
   1144 
   1145   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
   1146     QualType PointeeType = PT->getPointeeType();
   1147     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
   1148       SourceRange ArgRange = OrigArg->getSourceRange();
   1149       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
   1150 
   1151       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
   1152         commit.insertBefore(ArgRange.getBegin(), "@");
   1153       else
   1154         commit.insertWrap("@(", ArgRange, ")");
   1155 
   1156       return true;
   1157     }
   1158   }
   1159 
   1160   return false;
   1161 }
   1162 
   1163 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
   1164                                            const NSAPI &NS, Commit &commit) {
   1165   Selector Sel = Msg->getSelector();
   1166 
   1167   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
   1168       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
   1169       Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
   1170     if (Msg->getNumArgs() != 1)
   1171       return false;
   1172     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
   1173   }
   1174 
   1175   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
   1176     if (Msg->getNumArgs() != 2)
   1177       return false;
   1178 
   1179     const Expr *encodingArg = Msg->getArg(1);
   1180     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
   1181         NS.isNSASCIIStringEncodingConstant(encodingArg))
   1182       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
   1183   }
   1184 
   1185   return false;
   1186 }
   1187