xref: /freebsd/contrib/llvm-project/clang/lib/Edit/RewriteObjCFoundationAPI.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
10b57cec5SDimitry Andric //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Rewrites legacy method calls to modern syntax.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/Edit/Rewriters.h"
140b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
150b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
160b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
170b57cec5SDimitry Andric #include "clang/AST/NSAPI.h"
180b57cec5SDimitry Andric #include "clang/AST/ParentMap.h"
190b57cec5SDimitry Andric #include "clang/Edit/Commit.h"
200b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace clang;
230b57cec5SDimitry Andric using namespace edit;
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
260b57cec5SDimitry Andric                                     IdentifierInfo *&ClassId,
270b57cec5SDimitry Andric                                     const LangOptions &LangOpts) {
280b57cec5SDimitry Andric   if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
290b57cec5SDimitry Andric     return false;
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric   const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
320b57cec5SDimitry Andric   if (!Receiver)
330b57cec5SDimitry Andric     return false;
340b57cec5SDimitry Andric   ClassId = Receiver->getIdentifier();
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
370b57cec5SDimitry Andric     return true;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   // When in ARC mode we also convert "[[.. alloc] init]" messages to literals,
400b57cec5SDimitry Andric   // since the change from +1 to +0 will be handled fine by ARC.
410b57cec5SDimitry Andric   if (LangOpts.ObjCAutoRefCount) {
420b57cec5SDimitry Andric     if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) {
430b57cec5SDimitry Andric       if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>(
440b57cec5SDimitry Andric                            Msg->getInstanceReceiver()->IgnoreParenImpCasts())) {
450b57cec5SDimitry Andric         if (Rec->getMethodFamily() == OMF_alloc)
460b57cec5SDimitry Andric           return true;
470b57cec5SDimitry Andric       }
480b57cec5SDimitry Andric     }
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric   return false;
520b57cec5SDimitry Andric }
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
550b57cec5SDimitry Andric // rewriteObjCRedundantCallWithLiteral.
560b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
590b57cec5SDimitry Andric                                               const NSAPI &NS, Commit &commit) {
600b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
610b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
620b57cec5SDimitry Andric     return false;
630b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
640b57cec5SDimitry Andric     return false;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
670b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   if ((isa<ObjCStringLiteral>(Arg) &&
700b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
710b57cec5SDimitry Andric        (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel ||
720b57cec5SDimitry Andric         NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel))   ||
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric       (isa<ObjCArrayLiteral>(Arg) &&
750b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
760b57cec5SDimitry Andric        (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel ||
770b57cec5SDimitry Andric         NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel))     ||
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric       (isa<ObjCDictionaryLiteral>(Arg) &&
800b57cec5SDimitry Andric        NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
810b57cec5SDimitry Andric        (NS.getNSDictionarySelector(
820b57cec5SDimitry Andric                               NSAPI::NSDict_dictionaryWithDictionary) == Sel ||
830b57cec5SDimitry Andric         NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) {
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(),
860b57cec5SDimitry Andric                            Msg->getArg(0)->getSourceRange());
870b57cec5SDimitry Andric     return true;
880b57cec5SDimitry Andric   }
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   return false;
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
940b57cec5SDimitry Andric // rewriteToObjCSubscriptSyntax.
950b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric /// Check for classes that accept 'objectForKey:' (or the other selectors
980b57cec5SDimitry Andric /// that the migrator handles) but return their instances as 'id', resulting
990b57cec5SDimitry Andric /// in the compiler resolving 'objectForKey:' as the method from NSDictionary.
1000b57cec5SDimitry Andric ///
1010b57cec5SDimitry Andric /// When checking if we can convert to subscripting syntax, check whether
1020b57cec5SDimitry Andric /// the receiver is a result of a class method from a hardcoded list of
1030b57cec5SDimitry Andric /// such classes. In such a case return the specific class as the interface
1040b57cec5SDimitry Andric /// of the receiver.
1050b57cec5SDimitry Andric ///
1060b57cec5SDimitry Andric /// FIXME: Remove this when these classes start using 'instancetype'.
1070b57cec5SDimitry Andric static const ObjCInterfaceDecl *
1080b57cec5SDimitry Andric maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace,
1090b57cec5SDimitry Andric                                          const Expr *Receiver,
1100b57cec5SDimitry Andric                                          ASTContext &Ctx) {
1110b57cec5SDimitry Andric   assert(IFace && Receiver);
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   // If the receiver has type 'id'...
1140b57cec5SDimitry Andric   if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType()))
1150b57cec5SDimitry Andric     return IFace;
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   const ObjCMessageExpr *
1180b57cec5SDimitry Andric     InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts());
1190b57cec5SDimitry Andric   if (!InnerMsg)
1200b57cec5SDimitry Andric     return IFace;
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   QualType ClassRec;
1230b57cec5SDimitry Andric   switch (InnerMsg->getReceiverKind()) {
1240b57cec5SDimitry Andric   case ObjCMessageExpr::Instance:
1250b57cec5SDimitry Andric   case ObjCMessageExpr::SuperInstance:
1260b57cec5SDimitry Andric     return IFace;
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric   case ObjCMessageExpr::Class:
1290b57cec5SDimitry Andric     ClassRec = InnerMsg->getClassReceiver();
1300b57cec5SDimitry Andric     break;
1310b57cec5SDimitry Andric   case ObjCMessageExpr::SuperClass:
1320b57cec5SDimitry Andric     ClassRec = InnerMsg->getSuperType();
1330b57cec5SDimitry Andric     break;
1340b57cec5SDimitry Andric   }
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   if (ClassRec.isNull())
1370b57cec5SDimitry Andric     return IFace;
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric   // ...and it is the result of a class message...
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>();
1420b57cec5SDimitry Andric   if (!ObjTy)
1430b57cec5SDimitry Andric     return IFace;
1440b57cec5SDimitry Andric   const ObjCInterfaceDecl *OID = ObjTy->getInterface();
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   // ...and the receiving class is NSMapTable or NSLocale, return that
1470b57cec5SDimitry Andric   // class as the receiving interface.
1480b57cec5SDimitry Andric   if (OID->getName() == "NSMapTable" ||
1490b57cec5SDimitry Andric       OID->getName() == "NSLocale")
1500b57cec5SDimitry Andric     return OID;
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric   return IFace;
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace,
1560b57cec5SDimitry Andric                                         const ObjCMessageExpr *Msg,
1570b57cec5SDimitry Andric                                         ASTContext &Ctx,
1580b57cec5SDimitry Andric                                         Selector subscriptSel) {
1590b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
1600b57cec5SDimitry Andric   if (!Rec)
1610b57cec5SDimitry Andric     return false;
1620b57cec5SDimitry Andric   IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx);
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric   if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) {
1650b57cec5SDimitry Andric     if (!MD->isUnavailable())
1660b57cec5SDimitry Andric       return true;
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric   return false;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric static bool subscriptOperatorNeedsParens(const Expr *FullExpr);
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
1740b57cec5SDimitry Andric   if (subscriptOperatorNeedsParens(Receiver)) {
1750b57cec5SDimitry Andric     SourceRange RecRange = Receiver->getSourceRange();
1760b57cec5SDimitry Andric     commit.insertWrap("(", RecRange, ")");
1770b57cec5SDimitry Andric   }
1780b57cec5SDimitry Andric }
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg,
1810b57cec5SDimitry Andric                                         Commit &commit) {
1820b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
1830b57cec5SDimitry Andric     return false;
1840b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
1850b57cec5SDimitry Andric   if (!Rec)
1860b57cec5SDimitry Andric     return false;
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
1890b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
1900b57cec5SDimitry Andric   SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
1930b57cec5SDimitry Andric                                                        ArgRange.getBegin()),
1940b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
1950b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
1960b57cec5SDimitry Andric                          ArgRange);
1970b57cec5SDimitry Andric   commit.insertWrap("[", ArgRange, "]");
1980b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
1990b57cec5SDimitry Andric   return true;
2000b57cec5SDimitry Andric }
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace,
2030b57cec5SDimitry Andric                                        const ObjCMessageExpr *Msg,
2040b57cec5SDimitry Andric                                        const NSAPI &NS,
2050b57cec5SDimitry Andric                                        Commit &commit) {
2060b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
2070b57cec5SDimitry Andric                                    NS.getObjectAtIndexedSubscriptSelector()))
2080b57cec5SDimitry Andric     return false;
2090b57cec5SDimitry Andric   return rewriteToSubscriptGetCommon(Msg, commit);
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric 
2120b57cec5SDimitry Andric static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace,
2130b57cec5SDimitry Andric                                             const ObjCMessageExpr *Msg,
2140b57cec5SDimitry Andric                                             const NSAPI &NS,
2150b57cec5SDimitry Andric                                             Commit &commit) {
2160b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
2170b57cec5SDimitry Andric                                   NS.getObjectForKeyedSubscriptSelector()))
2180b57cec5SDimitry Andric     return false;
2190b57cec5SDimitry Andric   return rewriteToSubscriptGetCommon(Msg, commit);
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace,
2230b57cec5SDimitry Andric                                        const ObjCMessageExpr *Msg,
2240b57cec5SDimitry Andric                                        const NSAPI &NS,
2250b57cec5SDimitry Andric                                        Commit &commit) {
2260b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
2270b57cec5SDimitry Andric                                    NS.getSetObjectAtIndexedSubscriptSelector()))
2280b57cec5SDimitry Andric     return false;
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
2310b57cec5SDimitry Andric     return false;
2320b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
2330b57cec5SDimitry Andric   if (!Rec)
2340b57cec5SDimitry Andric     return false;
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
2370b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
2380b57cec5SDimitry Andric   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
2390b57cec5SDimitry Andric   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
2420b57cec5SDimitry Andric                                                        Arg0Range.getBegin()),
2430b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
2440b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
2450b57cec5SDimitry Andric                                                        Arg1Range.getBegin()),
2460b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(Arg0Range));
2470b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
2480b57cec5SDimitry Andric                          Arg1Range);
2490b57cec5SDimitry Andric   commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
2500b57cec5SDimitry Andric                                                        Arg1Range.getBegin()),
2510b57cec5SDimitry Andric                     "] = ");
2520b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
2530b57cec5SDimitry Andric   return true;
2540b57cec5SDimitry Andric }
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace,
2570b57cec5SDimitry Andric                                             const ObjCMessageExpr *Msg,
2580b57cec5SDimitry Andric                                             const NSAPI &NS,
2590b57cec5SDimitry Andric                                             Commit &commit) {
2600b57cec5SDimitry Andric   if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(),
2610b57cec5SDimitry Andric                                    NS.getSetObjectForKeyedSubscriptSelector()))
2620b57cec5SDimitry Andric     return false;
2630b57cec5SDimitry Andric 
2640b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
2650b57cec5SDimitry Andric     return false;
2660b57cec5SDimitry Andric   const Expr *Rec = Msg->getInstanceReceiver();
2670b57cec5SDimitry Andric   if (!Rec)
2680b57cec5SDimitry Andric     return false;
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
2710b57cec5SDimitry Andric   SourceRange RecRange = Rec->getSourceRange();
2720b57cec5SDimitry Andric   SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
2730b57cec5SDimitry Andric   SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric   SourceLocation LocBeforeVal = Arg0Range.getBegin();
2760b57cec5SDimitry Andric   commit.insertBefore(LocBeforeVal, "] = ");
2770b57cec5SDimitry Andric   commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
2780b57cec5SDimitry Andric                          /*beforePreviousInsertions=*/true);
2790b57cec5SDimitry Andric   commit.insertBefore(LocBeforeVal, "[");
2800b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
2810b57cec5SDimitry Andric                                                        Arg0Range.getBegin()),
2820b57cec5SDimitry Andric                          CharSourceRange::getTokenRange(RecRange));
2830b57cec5SDimitry Andric   commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
2840b57cec5SDimitry Andric                          Arg0Range);
2850b57cec5SDimitry Andric   maybePutParensOnReceiver(Rec, commit);
2860b57cec5SDimitry Andric   return true;
2870b57cec5SDimitry Andric }
2880b57cec5SDimitry Andric 
2890b57cec5SDimitry Andric bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
2900b57cec5SDimitry Andric                                         const NSAPI &NS, Commit &commit) {
2910b57cec5SDimitry Andric   if (!Msg || Msg->isImplicit() ||
2920b57cec5SDimitry Andric       Msg->getReceiverKind() != ObjCMessageExpr::Instance)
2930b57cec5SDimitry Andric     return false;
2940b57cec5SDimitry Andric   const ObjCMethodDecl *Method = Msg->getMethodDecl();
2950b57cec5SDimitry Andric   if (!Method)
2960b57cec5SDimitry Andric     return false;
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric   const ObjCInterfaceDecl *IFace =
2990b57cec5SDimitry Andric       NS.getASTContext().getObjContainingInterface(Method);
3000b57cec5SDimitry Andric   if (!IFace)
3010b57cec5SDimitry Andric     return false;
3020b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex))
3050b57cec5SDimitry Andric     return rewriteToArraySubscriptGet(IFace, Msg, NS, commit);
3060b57cec5SDimitry Andric 
3070b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))
3080b57cec5SDimitry Andric     return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit);
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric   if (Msg->getNumArgs() != 2)
3110b57cec5SDimitry Andric     return false;
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
3140b57cec5SDimitry Andric     return rewriteToArraySubscriptSet(IFace, Msg, NS, commit);
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
3170b57cec5SDimitry Andric     return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit);
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   return false;
3200b57cec5SDimitry Andric }
3210b57cec5SDimitry Andric 
3220b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3230b57cec5SDimitry Andric // rewriteToObjCLiteralSyntax.
3240b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
3270b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit,
3280b57cec5SDimitry Andric                                   const ParentMap *PMap);
3290b57cec5SDimitry Andric static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
3300b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit);
3310b57cec5SDimitry Andric static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
3320b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit);
3330b57cec5SDimitry Andric static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
3340b57cec5SDimitry Andric                                             const NSAPI &NS, Commit &commit);
3350b57cec5SDimitry Andric static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
3360b57cec5SDimitry Andric                                            const NSAPI &NS, Commit &commit);
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
3390b57cec5SDimitry Andric                                       const NSAPI &NS, Commit &commit,
3400b57cec5SDimitry Andric                                       const ParentMap *PMap) {
3410b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
3420b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
3430b57cec5SDimitry Andric     return false;
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
3460b57cec5SDimitry Andric     return rewriteToArrayLiteral(Msg, NS, commit, PMap);
3470b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
3480b57cec5SDimitry Andric     return rewriteToDictionaryLiteral(Msg, NS, commit);
3490b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
3500b57cec5SDimitry Andric     return rewriteToNumberLiteral(Msg, NS, commit);
3510b57cec5SDimitry Andric   if (II == NS.getNSClassId(NSAPI::ClassId_NSString))
3520b57cec5SDimitry Andric     return rewriteToStringBoxedExpression(Msg, NS, commit);
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric   return false;
3550b57cec5SDimitry Andric }
3560b57cec5SDimitry Andric 
3570b57cec5SDimitry Andric /// Returns true if the immediate message arguments of \c Msg should not
3580b57cec5SDimitry Andric /// be rewritten because it will interfere with the rewrite of the parent
3590b57cec5SDimitry Andric /// message expression. e.g.
3600b57cec5SDimitry Andric /// \code
3610b57cec5SDimitry Andric ///   [NSDictionary dictionaryWithObjects:
3620b57cec5SDimitry Andric ///                                 [NSArray arrayWithObjects:@"1", @"2", nil]
3630b57cec5SDimitry Andric ///                         forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]];
3640b57cec5SDimitry Andric /// \endcode
3650b57cec5SDimitry Andric /// It will return true for this because we are going to rewrite this directly
3660b57cec5SDimitry Andric /// to a dictionary literal without any array literals.
3670b57cec5SDimitry Andric static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
3680b57cec5SDimitry Andric                                                  const NSAPI &NS);
3690b57cec5SDimitry Andric 
3700b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3710b57cec5SDimitry Andric // rewriteToArrayLiteral.
3720b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric /// Adds an explicit cast to 'id' if the type is not objc object.
3750b57cec5SDimitry Andric static void objectifyExpr(const Expr *E, Commit &commit);
3760b57cec5SDimitry Andric 
3770b57cec5SDimitry Andric static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
3780b57cec5SDimitry Andric                                   const NSAPI &NS, Commit &commit,
3790b57cec5SDimitry Andric                                   const ParentMap *PMap) {
3800b57cec5SDimitry Andric   if (PMap) {
3810b57cec5SDimitry Andric     const ObjCMessageExpr *ParentMsg =
3820b57cec5SDimitry Andric         dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg));
3830b57cec5SDimitry Andric     if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS))
3840b57cec5SDimitry Andric       return false;
3850b57cec5SDimitry Andric   }
3860b57cec5SDimitry Andric 
3870b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
3880b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
3910b57cec5SDimitry Andric     if (Msg->getNumArgs() != 0)
3920b57cec5SDimitry Andric       return false;
3930b57cec5SDimitry Andric     commit.replace(MsgRange, "@[]");
3940b57cec5SDimitry Andric     return true;
3950b57cec5SDimitry Andric   }
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
3980b57cec5SDimitry Andric     if (Msg->getNumArgs() != 1)
3990b57cec5SDimitry Andric       return false;
4000b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(0), commit);
4010b57cec5SDimitry Andric     SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
4020b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
4030b57cec5SDimitry Andric     commit.insertWrap("@[", ArgRange, "]");
4040b57cec5SDimitry Andric     return true;
4050b57cec5SDimitry Andric   }
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric   if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
4080b57cec5SDimitry Andric       Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
4090b57cec5SDimitry Andric     if (Msg->getNumArgs() == 0)
4100b57cec5SDimitry Andric       return false;
4110b57cec5SDimitry Andric     const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
4120b57cec5SDimitry Andric     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
4130b57cec5SDimitry Andric       return false;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric     for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
4160b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i), commit);
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric     if (Msg->getNumArgs() == 1) {
4190b57cec5SDimitry Andric       commit.replace(MsgRange, "@[]");
4200b57cec5SDimitry Andric       return true;
4210b57cec5SDimitry Andric     }
4220b57cec5SDimitry Andric     SourceRange ArgRange(Msg->getArg(0)->getBeginLoc(),
4230b57cec5SDimitry Andric                          Msg->getArg(Msg->getNumArgs() - 2)->getEndLoc());
4240b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
4250b57cec5SDimitry Andric     commit.insertWrap("@[", ArgRange, "]");
4260b57cec5SDimitry Andric     return true;
4270b57cec5SDimitry Andric   }
4280b57cec5SDimitry Andric 
4290b57cec5SDimitry Andric   return false;
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric 
4320b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4330b57cec5SDimitry Andric // rewriteToDictionaryLiteral.
4340b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4350b57cec5SDimitry Andric 
4360b57cec5SDimitry Andric /// If \c Msg is an NSArray creation message or literal, this gets the
4370b57cec5SDimitry Andric /// objects that were used to create it.
4380b57cec5SDimitry Andric /// \returns true if it is an NSArray and we got objects, or false otherwise.
4390b57cec5SDimitry Andric static bool getNSArrayObjects(const Expr *E, const NSAPI &NS,
4400b57cec5SDimitry Andric                               SmallVectorImpl<const Expr *> &Objs) {
4410b57cec5SDimitry Andric   if (!E)
4420b57cec5SDimitry Andric     return false;
4430b57cec5SDimitry Andric 
4440b57cec5SDimitry Andric   E = E->IgnoreParenCasts();
4450b57cec5SDimitry Andric   if (!E)
4460b57cec5SDimitry Andric     return false;
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric   if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
4490b57cec5SDimitry Andric     IdentifierInfo *Cls = nullptr;
4500b57cec5SDimitry Andric     if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts()))
4510b57cec5SDimitry Andric       return false;
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric     if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray))
4540b57cec5SDimitry Andric       return false;
4550b57cec5SDimitry Andric 
4560b57cec5SDimitry Andric     Selector Sel = Msg->getSelector();
4570b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array))
4580b57cec5SDimitry Andric       return true; // empty array.
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
4610b57cec5SDimitry Andric       if (Msg->getNumArgs() != 1)
4620b57cec5SDimitry Andric         return false;
4630b57cec5SDimitry Andric       Objs.push_back(Msg->getArg(0));
4640b57cec5SDimitry Andric       return true;
4650b57cec5SDimitry Andric     }
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric     if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) ||
4680b57cec5SDimitry Andric         Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) {
4690b57cec5SDimitry Andric       if (Msg->getNumArgs() == 0)
4700b57cec5SDimitry Andric         return false;
4710b57cec5SDimitry Andric       const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
4720b57cec5SDimitry Andric       if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
4730b57cec5SDimitry Andric         return false;
4740b57cec5SDimitry Andric 
4750b57cec5SDimitry Andric       for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i)
4760b57cec5SDimitry Andric         Objs.push_back(Msg->getArg(i));
4770b57cec5SDimitry Andric       return true;
4780b57cec5SDimitry Andric     }
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric   } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) {
4810b57cec5SDimitry Andric     for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i)
4820b57cec5SDimitry Andric       Objs.push_back(ArrLit->getElement(i));
4830b57cec5SDimitry Andric     return true;
4840b57cec5SDimitry Andric   }
4850b57cec5SDimitry Andric 
4860b57cec5SDimitry Andric   return false;
4870b57cec5SDimitry Andric }
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
4900b57cec5SDimitry Andric                                        const NSAPI &NS, Commit &commit) {
4910b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
4920b57cec5SDimitry Andric   SourceRange MsgRange = Msg->getSourceRange();
4930b57cec5SDimitry Andric 
4940b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
4950b57cec5SDimitry Andric     if (Msg->getNumArgs() != 0)
4960b57cec5SDimitry Andric       return false;
4970b57cec5SDimitry Andric     commit.replace(MsgRange, "@{}");
4980b57cec5SDimitry Andric     return true;
4990b57cec5SDimitry Andric   }
5000b57cec5SDimitry Andric 
5010b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
5020b57cec5SDimitry Andric                                     NSAPI::NSDict_dictionaryWithObjectForKey)) {
5030b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
5040b57cec5SDimitry Andric       return false;
5050b57cec5SDimitry Andric 
5060b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(0), commit);
5070b57cec5SDimitry Andric     objectifyExpr(Msg->getArg(1), commit);
5080b57cec5SDimitry Andric 
5090b57cec5SDimitry Andric     SourceRange ValRange = Msg->getArg(0)->getSourceRange();
5100b57cec5SDimitry Andric     SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
5110b57cec5SDimitry Andric     // Insert key before the value.
5120b57cec5SDimitry Andric     commit.insertBefore(ValRange.getBegin(), ": ");
5130b57cec5SDimitry Andric     commit.insertFromRange(ValRange.getBegin(),
5140b57cec5SDimitry Andric                            CharSourceRange::getTokenRange(KeyRange),
5150b57cec5SDimitry Andric                        /*afterToken=*/false, /*beforePreviousInsertions=*/true);
5160b57cec5SDimitry Andric     commit.insertBefore(ValRange.getBegin(), "@{");
5170b57cec5SDimitry Andric     commit.insertAfterToken(ValRange.getEnd(), "}");
5180b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ValRange);
5190b57cec5SDimitry Andric     return true;
5200b57cec5SDimitry Andric   }
5210b57cec5SDimitry Andric 
5220b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
5230b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsAndKeys) ||
5240b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) {
5250b57cec5SDimitry Andric     if (Msg->getNumArgs() % 2 != 1)
5260b57cec5SDimitry Andric       return false;
5270b57cec5SDimitry Andric     unsigned SentinelIdx = Msg->getNumArgs() - 1;
5280b57cec5SDimitry Andric     const Expr *SentinelExpr = Msg->getArg(SentinelIdx);
5290b57cec5SDimitry Andric     if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
5300b57cec5SDimitry Andric       return false;
5310b57cec5SDimitry Andric 
5320b57cec5SDimitry Andric     if (Msg->getNumArgs() == 1) {
5330b57cec5SDimitry Andric       commit.replace(MsgRange, "@{}");
5340b57cec5SDimitry Andric       return true;
5350b57cec5SDimitry Andric     }
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric     for (unsigned i = 0; i < SentinelIdx; i += 2) {
5380b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i), commit);
5390b57cec5SDimitry Andric       objectifyExpr(Msg->getArg(i+1), commit);
5400b57cec5SDimitry Andric 
5410b57cec5SDimitry Andric       SourceRange ValRange = Msg->getArg(i)->getSourceRange();
5420b57cec5SDimitry Andric       SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange();
5430b57cec5SDimitry Andric       // Insert value after key.
5440b57cec5SDimitry Andric       commit.insertAfterToken(KeyRange.getEnd(), ": ");
5450b57cec5SDimitry Andric       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
5460b57cec5SDimitry Andric       commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(),
5470b57cec5SDimitry Andric                                                   KeyRange.getBegin()));
5480b57cec5SDimitry Andric     }
5490b57cec5SDimitry Andric     // Range of arguments up until and including the last key.
5500b57cec5SDimitry Andric     // The sentinel and first value are cut off, the value will move after the
5510b57cec5SDimitry Andric     // key.
5520b57cec5SDimitry Andric     SourceRange ArgRange(Msg->getArg(1)->getBeginLoc(),
5530b57cec5SDimitry Andric                          Msg->getArg(SentinelIdx - 1)->getEndLoc());
5540b57cec5SDimitry Andric     commit.insertWrap("@{", ArgRange, "}");
5550b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
5560b57cec5SDimitry Andric     return true;
5570b57cec5SDimitry Andric   }
5580b57cec5SDimitry Andric 
5590b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
5600b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
5610b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
5620b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
5630b57cec5SDimitry Andric       return false;
5640b57cec5SDimitry Andric 
5650b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Vals;
5660b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
5670b57cec5SDimitry Andric       return false;
5680b57cec5SDimitry Andric 
5690b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Keys;
5700b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
5710b57cec5SDimitry Andric       return false;
5720b57cec5SDimitry Andric 
5730b57cec5SDimitry Andric     if (Vals.size() != Keys.size())
5740b57cec5SDimitry Andric       return false;
5750b57cec5SDimitry Andric 
5760b57cec5SDimitry Andric     if (Vals.empty()) {
5770b57cec5SDimitry Andric       commit.replace(MsgRange, "@{}");
5780b57cec5SDimitry Andric       return true;
5790b57cec5SDimitry Andric     }
5800b57cec5SDimitry Andric 
5810b57cec5SDimitry Andric     for (unsigned i = 0, n = Vals.size(); i < n; ++i) {
5820b57cec5SDimitry Andric       objectifyExpr(Vals[i], commit);
5830b57cec5SDimitry Andric       objectifyExpr(Keys[i], commit);
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric       SourceRange ValRange = Vals[i]->getSourceRange();
5860b57cec5SDimitry Andric       SourceRange KeyRange = Keys[i]->getSourceRange();
5870b57cec5SDimitry Andric       // Insert value after key.
5880b57cec5SDimitry Andric       commit.insertAfterToken(KeyRange.getEnd(), ": ");
5890b57cec5SDimitry Andric       commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true);
5900b57cec5SDimitry Andric     }
5910b57cec5SDimitry Andric     // Range of arguments up until and including the last key.
5920b57cec5SDimitry Andric     // The first value is cut off, the value will move after the key.
5930b57cec5SDimitry Andric     SourceRange ArgRange(Keys.front()->getBeginLoc(), Keys.back()->getEndLoc());
5940b57cec5SDimitry Andric     commit.insertWrap("@{", ArgRange, "}");
5950b57cec5SDimitry Andric     commit.replaceWithInner(MsgRange, ArgRange);
5960b57cec5SDimitry Andric     return true;
5970b57cec5SDimitry Andric   }
5980b57cec5SDimitry Andric 
5990b57cec5SDimitry Andric   return false;
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg,
6030b57cec5SDimitry Andric                                                  const NSAPI &NS) {
6040b57cec5SDimitry Andric   if (!Msg)
6050b57cec5SDimitry Andric     return false;
6060b57cec5SDimitry Andric 
6070b57cec5SDimitry Andric   IdentifierInfo *II = nullptr;
6080b57cec5SDimitry Andric   if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts()))
6090b57cec5SDimitry Andric     return false;
6100b57cec5SDimitry Andric 
6110b57cec5SDimitry Andric   if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary))
6120b57cec5SDimitry Andric     return false;
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
6150b57cec5SDimitry Andric   if (Sel == NS.getNSDictionarySelector(
6160b57cec5SDimitry Andric                                   NSAPI::NSDict_dictionaryWithObjectsForKeys) ||
6170b57cec5SDimitry Andric       Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) {
6180b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
6190b57cec5SDimitry Andric       return false;
6200b57cec5SDimitry Andric 
6210b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Vals;
6220b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(0), NS, Vals))
6230b57cec5SDimitry Andric       return false;
6240b57cec5SDimitry Andric 
6250b57cec5SDimitry Andric     SmallVector<const Expr *, 8> Keys;
6260b57cec5SDimitry Andric     if (!getNSArrayObjects(Msg->getArg(1), NS, Keys))
6270b57cec5SDimitry Andric       return false;
6280b57cec5SDimitry Andric 
6290b57cec5SDimitry Andric     if (Vals.size() != Keys.size())
6300b57cec5SDimitry Andric       return false;
6310b57cec5SDimitry Andric 
6320b57cec5SDimitry Andric     return true;
6330b57cec5SDimitry Andric   }
6340b57cec5SDimitry Andric 
6350b57cec5SDimitry Andric   return false;
6360b57cec5SDimitry Andric }
6370b57cec5SDimitry Andric 
6380b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
6390b57cec5SDimitry Andric // rewriteToNumberLiteral.
6400b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
6410b57cec5SDimitry Andric 
6420b57cec5SDimitry Andric static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg,
6430b57cec5SDimitry Andric                                    const CharacterLiteral *Arg,
6440b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
6450b57cec5SDimitry Andric   if (Arg->getKind() != CharacterLiteral::Ascii)
6460b57cec5SDimitry Andric     return false;
6470b57cec5SDimitry Andric   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar,
6480b57cec5SDimitry Andric                                    Msg->getSelector())) {
6490b57cec5SDimitry Andric     SourceRange ArgRange = Arg->getSourceRange();
6500b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
6510b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
6520b57cec5SDimitry Andric     return true;
6530b57cec5SDimitry Andric   }
6540b57cec5SDimitry Andric 
6550b57cec5SDimitry Andric   return rewriteToNumericBoxedExpression(Msg, NS, commit);
6560b57cec5SDimitry Andric }
6570b57cec5SDimitry Andric 
6580b57cec5SDimitry Andric static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg,
6590b57cec5SDimitry Andric                                    const Expr *Arg,
6600b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
6610b57cec5SDimitry Andric   if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool,
6620b57cec5SDimitry Andric                                    Msg->getSelector())) {
6630b57cec5SDimitry Andric     SourceRange ArgRange = Arg->getSourceRange();
6640b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
6650b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
6660b57cec5SDimitry Andric     return true;
6670b57cec5SDimitry Andric   }
6680b57cec5SDimitry Andric 
6690b57cec5SDimitry Andric   return rewriteToNumericBoxedExpression(Msg, NS, commit);
6700b57cec5SDimitry Andric }
6710b57cec5SDimitry Andric 
6720b57cec5SDimitry Andric namespace {
6730b57cec5SDimitry Andric 
6740b57cec5SDimitry Andric struct LiteralInfo {
6750b57cec5SDimitry Andric   bool Hex, Octal;
6760b57cec5SDimitry Andric   StringRef U, F, L, LL;
6770b57cec5SDimitry Andric   CharSourceRange WithoutSuffRange;
6780b57cec5SDimitry Andric };
6790b57cec5SDimitry Andric 
6800b57cec5SDimitry Andric }
6810b57cec5SDimitry Andric 
6820b57cec5SDimitry Andric static bool getLiteralInfo(SourceRange literalRange,
6830b57cec5SDimitry Andric                            bool isFloat, bool isIntZero,
6840b57cec5SDimitry Andric                           ASTContext &Ctx, LiteralInfo &Info) {
6850b57cec5SDimitry Andric   if (literalRange.getBegin().isMacroID() ||
6860b57cec5SDimitry Andric       literalRange.getEnd().isMacroID())
6870b57cec5SDimitry Andric     return false;
6880b57cec5SDimitry Andric   StringRef text = Lexer::getSourceText(
6890b57cec5SDimitry Andric                                   CharSourceRange::getTokenRange(literalRange),
6900b57cec5SDimitry Andric                                   Ctx.getSourceManager(), Ctx.getLangOpts());
6910b57cec5SDimitry Andric   if (text.empty())
6920b57cec5SDimitry Andric     return false;
6930b57cec5SDimitry Andric 
6940b57cec5SDimitry Andric   Optional<bool> UpperU, UpperL;
6950b57cec5SDimitry Andric   bool UpperF = false;
6960b57cec5SDimitry Andric 
6970b57cec5SDimitry Andric   struct Suff {
6980b57cec5SDimitry Andric     static bool has(StringRef suff, StringRef &text) {
6990b57cec5SDimitry Andric       if (text.endswith(suff)) {
7000b57cec5SDimitry Andric         text = text.substr(0, text.size()-suff.size());
7010b57cec5SDimitry Andric         return true;
7020b57cec5SDimitry Andric       }
7030b57cec5SDimitry Andric       return false;
7040b57cec5SDimitry Andric     }
7050b57cec5SDimitry Andric   };
7060b57cec5SDimitry Andric 
7070b57cec5SDimitry Andric   while (1) {
7080b57cec5SDimitry Andric     if (Suff::has("u", text)) {
7090b57cec5SDimitry Andric       UpperU = false;
7100b57cec5SDimitry Andric     } else if (Suff::has("U", text)) {
7110b57cec5SDimitry Andric       UpperU = true;
7120b57cec5SDimitry Andric     } else if (Suff::has("ll", text)) {
7130b57cec5SDimitry Andric       UpperL = false;
7140b57cec5SDimitry Andric     } else if (Suff::has("LL", text)) {
7150b57cec5SDimitry Andric       UpperL = true;
7160b57cec5SDimitry Andric     } else if (Suff::has("l", text)) {
7170b57cec5SDimitry Andric       UpperL = false;
7180b57cec5SDimitry Andric     } else if (Suff::has("L", text)) {
7190b57cec5SDimitry Andric       UpperL = true;
7200b57cec5SDimitry Andric     } else if (isFloat && Suff::has("f", text)) {
7210b57cec5SDimitry Andric       UpperF = false;
7220b57cec5SDimitry Andric     } else if (isFloat && Suff::has("F", text)) {
7230b57cec5SDimitry Andric       UpperF = true;
7240b57cec5SDimitry Andric     } else
7250b57cec5SDimitry Andric       break;
7260b57cec5SDimitry Andric   }
7270b57cec5SDimitry Andric 
7280b57cec5SDimitry Andric   if (!UpperU.hasValue() && !UpperL.hasValue())
7290b57cec5SDimitry Andric     UpperU = UpperL = true;
7300b57cec5SDimitry Andric   else if (UpperU.hasValue() && !UpperL.hasValue())
7310b57cec5SDimitry Andric     UpperL = UpperU;
7320b57cec5SDimitry Andric   else if (UpperL.hasValue() && !UpperU.hasValue())
7330b57cec5SDimitry Andric     UpperU = UpperL;
7340b57cec5SDimitry Andric 
7350b57cec5SDimitry Andric   Info.U = *UpperU ? "U" : "u";
7360b57cec5SDimitry Andric   Info.L = *UpperL ? "L" : "l";
7370b57cec5SDimitry Andric   Info.LL = *UpperL ? "LL" : "ll";
7380b57cec5SDimitry Andric   Info.F = UpperF ? "F" : "f";
7390b57cec5SDimitry Andric 
7400b57cec5SDimitry Andric   Info.Hex = Info.Octal = false;
7410b57cec5SDimitry Andric   if (text.startswith("0x"))
7420b57cec5SDimitry Andric     Info.Hex = true;
7430b57cec5SDimitry Andric   else if (!isFloat && !isIntZero && text.startswith("0"))
7440b57cec5SDimitry Andric     Info.Octal = true;
7450b57cec5SDimitry Andric 
7460b57cec5SDimitry Andric   SourceLocation B = literalRange.getBegin();
7470b57cec5SDimitry Andric   Info.WithoutSuffRange =
7480b57cec5SDimitry Andric       CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size()));
7490b57cec5SDimitry Andric   return true;
7500b57cec5SDimitry Andric }
7510b57cec5SDimitry Andric 
7520b57cec5SDimitry Andric static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
7530b57cec5SDimitry Andric                                    const NSAPI &NS, Commit &commit) {
7540b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
7550b57cec5SDimitry Andric     return false;
7560b57cec5SDimitry Andric 
7570b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
7580b57cec5SDimitry Andric   if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg))
7590b57cec5SDimitry Andric     return rewriteToCharLiteral(Msg, CharE, NS, commit);
7600b57cec5SDimitry Andric   if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg))
7610b57cec5SDimitry Andric     return rewriteToBoolLiteral(Msg, BE, NS, commit);
7620b57cec5SDimitry Andric   if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg))
7630b57cec5SDimitry Andric     return rewriteToBoolLiteral(Msg, BE, NS, commit);
7640b57cec5SDimitry Andric 
7650b57cec5SDimitry Andric   const Expr *literalE = Arg;
7660b57cec5SDimitry Andric   if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) {
7670b57cec5SDimitry Andric     if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus)
7680b57cec5SDimitry Andric       literalE = UOE->getSubExpr();
7690b57cec5SDimitry Andric   }
7700b57cec5SDimitry Andric 
7710b57cec5SDimitry Andric   // Only integer and floating literals, otherwise try to rewrite to boxed
7720b57cec5SDimitry Andric   // expression.
7730b57cec5SDimitry Andric   if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE))
7740b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
7750b57cec5SDimitry Andric 
7760b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
7770b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
7780b57cec5SDimitry Andric   Optional<NSAPI::NSNumberLiteralMethodKind>
7790b57cec5SDimitry Andric     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
7800b57cec5SDimitry Andric   if (!MKOpt)
7810b57cec5SDimitry Andric     return false;
7820b57cec5SDimitry Andric   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
7830b57cec5SDimitry Andric 
7840b57cec5SDimitry Andric   bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false;
7850b57cec5SDimitry Andric   bool CallIsFloating = false, CallIsDouble = false;
7860b57cec5SDimitry Andric 
7870b57cec5SDimitry Andric   switch (MK) {
7880b57cec5SDimitry Andric   // We cannot have these calls with int/float literals.
7890b57cec5SDimitry Andric   case NSAPI::NSNumberWithChar:
7900b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedChar:
7910b57cec5SDimitry Andric   case NSAPI::NSNumberWithShort:
7920b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedShort:
7930b57cec5SDimitry Andric   case NSAPI::NSNumberWithBool:
7940b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
7950b57cec5SDimitry Andric 
7960b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedInt:
7970b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedInteger:
7980b57cec5SDimitry Andric     CallIsUnsigned = true;
7990b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
8000b57cec5SDimitry Andric   case NSAPI::NSNumberWithInt:
8010b57cec5SDimitry Andric   case NSAPI::NSNumberWithInteger:
8020b57cec5SDimitry Andric     break;
8030b57cec5SDimitry Andric 
8040b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedLong:
8050b57cec5SDimitry Andric     CallIsUnsigned = true;
8060b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
8070b57cec5SDimitry Andric   case NSAPI::NSNumberWithLong:
8080b57cec5SDimitry Andric     CallIsLong = true;
8090b57cec5SDimitry Andric     break;
8100b57cec5SDimitry Andric 
8110b57cec5SDimitry Andric   case NSAPI::NSNumberWithUnsignedLongLong:
8120b57cec5SDimitry Andric     CallIsUnsigned = true;
8130b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
8140b57cec5SDimitry Andric   case NSAPI::NSNumberWithLongLong:
8150b57cec5SDimitry Andric     CallIsLongLong = true;
8160b57cec5SDimitry Andric     break;
8170b57cec5SDimitry Andric 
8180b57cec5SDimitry Andric   case NSAPI::NSNumberWithDouble:
8190b57cec5SDimitry Andric     CallIsDouble = true;
8200b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
8210b57cec5SDimitry Andric   case NSAPI::NSNumberWithFloat:
8220b57cec5SDimitry Andric     CallIsFloating = true;
8230b57cec5SDimitry Andric     break;
8240b57cec5SDimitry Andric   }
8250b57cec5SDimitry Andric 
8260b57cec5SDimitry Andric   SourceRange ArgRange = Arg->getSourceRange();
8270b57cec5SDimitry Andric   QualType ArgTy = Arg->getType();
8280b57cec5SDimitry Andric   QualType CallTy = Msg->getArg(0)->getType();
8290b57cec5SDimitry Andric 
8300b57cec5SDimitry Andric   // Check for the easy case, the literal maps directly to the call.
8310b57cec5SDimitry Andric   if (Ctx.hasSameType(ArgTy, CallTy)) {
8320b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
8330b57cec5SDimitry Andric     commit.insert(ArgRange.getBegin(), "@");
8340b57cec5SDimitry Andric     return true;
8350b57cec5SDimitry Andric   }
8360b57cec5SDimitry Andric 
8370b57cec5SDimitry Andric   // We will need to modify the literal suffix to get the same type as the call.
8380b57cec5SDimitry Andric   // Try with boxed expression if it came from a macro.
8390b57cec5SDimitry Andric   if (ArgRange.getBegin().isMacroID())
8400b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
8410b57cec5SDimitry Andric 
8420b57cec5SDimitry Andric   bool LitIsFloat = ArgTy->isFloatingType();
8430b57cec5SDimitry Andric   // For a float passed to integer call, don't try rewriting to objc literal.
8440b57cec5SDimitry Andric   // It is difficult and a very uncommon case anyway.
8450b57cec5SDimitry Andric   // But try with boxed expression.
8460b57cec5SDimitry Andric   if (LitIsFloat && !CallIsFloating)
8470b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
8480b57cec5SDimitry Andric 
8490b57cec5SDimitry Andric   // Try to modify the literal make it the same type as the method call.
8500b57cec5SDimitry Andric   // -Modify the suffix, and/or
8510b57cec5SDimitry Andric   // -Change integer to float
8520b57cec5SDimitry Andric 
8530b57cec5SDimitry Andric   LiteralInfo LitInfo;
8540b57cec5SDimitry Andric   bool isIntZero = false;
8550b57cec5SDimitry Andric   if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE))
8560b57cec5SDimitry Andric     isIntZero = !IntE->getValue().getBoolValue();
8570b57cec5SDimitry Andric   if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo))
8580b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
8590b57cec5SDimitry Andric 
8600b57cec5SDimitry Andric   // Not easy to do int -> float with hex/octal and uncommon anyway.
8610b57cec5SDimitry Andric   if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal))
8620b57cec5SDimitry Andric     return rewriteToNumericBoxedExpression(Msg, NS, commit);
8630b57cec5SDimitry Andric 
8640b57cec5SDimitry Andric   SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin();
8650b57cec5SDimitry Andric   SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd();
8660b57cec5SDimitry Andric 
8670b57cec5SDimitry Andric   commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()),
8680b57cec5SDimitry Andric                          LitInfo.WithoutSuffRange);
8690b57cec5SDimitry Andric   commit.insert(LitB, "@");
8700b57cec5SDimitry Andric 
8710b57cec5SDimitry Andric   if (!LitIsFloat && CallIsFloating)
8720b57cec5SDimitry Andric     commit.insert(LitE, ".0");
8730b57cec5SDimitry Andric 
8740b57cec5SDimitry Andric   if (CallIsFloating) {
8750b57cec5SDimitry Andric     if (!CallIsDouble)
8760b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.F);
8770b57cec5SDimitry Andric   } else {
8780b57cec5SDimitry Andric     if (CallIsUnsigned)
8790b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.U);
8800b57cec5SDimitry Andric 
8810b57cec5SDimitry Andric     if (CallIsLong)
8820b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.L);
8830b57cec5SDimitry Andric     else if (CallIsLongLong)
8840b57cec5SDimitry Andric       commit.insert(LitE, LitInfo.LL);
8850b57cec5SDimitry Andric   }
8860b57cec5SDimitry Andric   return true;
8870b57cec5SDimitry Andric }
8880b57cec5SDimitry Andric 
8890b57cec5SDimitry Andric // FIXME: Make determination of operator precedence more general and
8900b57cec5SDimitry Andric // make it broadly available.
8910b57cec5SDimitry Andric static bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
8920b57cec5SDimitry Andric   const Expr* Expr = FullExpr->IgnoreImpCasts();
8930b57cec5SDimitry Andric   if (isa<ArraySubscriptExpr>(Expr) ||
8940b57cec5SDimitry Andric       isa<CallExpr>(Expr) ||
8950b57cec5SDimitry Andric       isa<DeclRefExpr>(Expr) ||
8960b57cec5SDimitry Andric       isa<CXXNamedCastExpr>(Expr) ||
8970b57cec5SDimitry Andric       isa<CXXConstructExpr>(Expr) ||
8980b57cec5SDimitry Andric       isa<CXXThisExpr>(Expr) ||
8990b57cec5SDimitry Andric       isa<CXXTypeidExpr>(Expr) ||
9000b57cec5SDimitry Andric       isa<CXXUnresolvedConstructExpr>(Expr) ||
9010b57cec5SDimitry Andric       isa<ObjCMessageExpr>(Expr) ||
9020b57cec5SDimitry Andric       isa<ObjCPropertyRefExpr>(Expr) ||
9030b57cec5SDimitry Andric       isa<ObjCProtocolExpr>(Expr) ||
9040b57cec5SDimitry Andric       isa<MemberExpr>(Expr) ||
9050b57cec5SDimitry Andric       isa<ObjCIvarRefExpr>(Expr) ||
9060b57cec5SDimitry Andric       isa<ParenExpr>(FullExpr) ||
9070b57cec5SDimitry Andric       isa<ParenListExpr>(Expr) ||
9080b57cec5SDimitry Andric       isa<SizeOfPackExpr>(Expr))
9090b57cec5SDimitry Andric     return false;
9100b57cec5SDimitry Andric 
9110b57cec5SDimitry Andric   return true;
9120b57cec5SDimitry Andric }
9130b57cec5SDimitry Andric static bool castOperatorNeedsParens(const Expr *FullExpr) {
9140b57cec5SDimitry Andric   const Expr* Expr = FullExpr->IgnoreImpCasts();
9150b57cec5SDimitry Andric   if (isa<ArraySubscriptExpr>(Expr) ||
9160b57cec5SDimitry Andric       isa<CallExpr>(Expr) ||
9170b57cec5SDimitry Andric       isa<DeclRefExpr>(Expr) ||
9180b57cec5SDimitry Andric       isa<CastExpr>(Expr) ||
9190b57cec5SDimitry Andric       isa<CXXNewExpr>(Expr) ||
9200b57cec5SDimitry Andric       isa<CXXConstructExpr>(Expr) ||
9210b57cec5SDimitry Andric       isa<CXXDeleteExpr>(Expr) ||
9220b57cec5SDimitry Andric       isa<CXXNoexceptExpr>(Expr) ||
9230b57cec5SDimitry Andric       isa<CXXPseudoDestructorExpr>(Expr) ||
9240b57cec5SDimitry Andric       isa<CXXScalarValueInitExpr>(Expr) ||
9250b57cec5SDimitry Andric       isa<CXXThisExpr>(Expr) ||
9260b57cec5SDimitry Andric       isa<CXXTypeidExpr>(Expr) ||
9270b57cec5SDimitry Andric       isa<CXXUnresolvedConstructExpr>(Expr) ||
9280b57cec5SDimitry Andric       isa<ObjCMessageExpr>(Expr) ||
9290b57cec5SDimitry Andric       isa<ObjCPropertyRefExpr>(Expr) ||
9300b57cec5SDimitry Andric       isa<ObjCProtocolExpr>(Expr) ||
9310b57cec5SDimitry Andric       isa<MemberExpr>(Expr) ||
9320b57cec5SDimitry Andric       isa<ObjCIvarRefExpr>(Expr) ||
9330b57cec5SDimitry Andric       isa<ParenExpr>(FullExpr) ||
9340b57cec5SDimitry Andric       isa<ParenListExpr>(Expr) ||
9350b57cec5SDimitry Andric       isa<SizeOfPackExpr>(Expr) ||
9360b57cec5SDimitry Andric       isa<UnaryOperator>(Expr))
9370b57cec5SDimitry Andric     return false;
9380b57cec5SDimitry Andric 
9390b57cec5SDimitry Andric   return true;
9400b57cec5SDimitry Andric }
9410b57cec5SDimitry Andric 
9420b57cec5SDimitry Andric static void objectifyExpr(const Expr *E, Commit &commit) {
9430b57cec5SDimitry Andric   if (!E) return;
9440b57cec5SDimitry Andric 
9450b57cec5SDimitry Andric   QualType T = E->getType();
9460b57cec5SDimitry Andric   if (T->isObjCObjectPointerType()) {
9470b57cec5SDimitry Andric     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
9480b57cec5SDimitry Andric       if (ICE->getCastKind() != CK_CPointerToObjCPointerCast)
9490b57cec5SDimitry Andric         return;
9500b57cec5SDimitry Andric     } else {
9510b57cec5SDimitry Andric       return;
9520b57cec5SDimitry Andric     }
9530b57cec5SDimitry Andric   } else if (!T->isPointerType()) {
9540b57cec5SDimitry Andric     return;
9550b57cec5SDimitry Andric   }
9560b57cec5SDimitry Andric 
9570b57cec5SDimitry Andric   SourceRange Range = E->getSourceRange();
9580b57cec5SDimitry Andric   if (castOperatorNeedsParens(E))
9590b57cec5SDimitry Andric     commit.insertWrap("(", Range, ")");
9600b57cec5SDimitry Andric   commit.insertBefore(Range.getBegin(), "(id)");
9610b57cec5SDimitry Andric }
9620b57cec5SDimitry Andric 
9630b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
9640b57cec5SDimitry Andric // rewriteToNumericBoxedExpression.
9650b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
9660b57cec5SDimitry Andric 
9670b57cec5SDimitry Andric static bool isEnumConstant(const Expr *E) {
9680b57cec5SDimitry Andric   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
9690b57cec5SDimitry Andric     if (const ValueDecl *VD = DRE->getDecl())
9700b57cec5SDimitry Andric       return isa<EnumConstantDecl>(VD);
9710b57cec5SDimitry Andric 
9720b57cec5SDimitry Andric   return false;
9730b57cec5SDimitry Andric }
9740b57cec5SDimitry Andric 
9750b57cec5SDimitry Andric static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
9760b57cec5SDimitry Andric                                             const NSAPI &NS, Commit &commit) {
9770b57cec5SDimitry Andric   if (Msg->getNumArgs() != 1)
9780b57cec5SDimitry Andric     return false;
9790b57cec5SDimitry Andric 
9800b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0);
9810b57cec5SDimitry Andric   if (Arg->isTypeDependent())
9820b57cec5SDimitry Andric     return false;
9830b57cec5SDimitry Andric 
9840b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
9850b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
9860b57cec5SDimitry Andric   Optional<NSAPI::NSNumberLiteralMethodKind>
9870b57cec5SDimitry Andric     MKOpt = NS.getNSNumberLiteralMethodKind(Sel);
9880b57cec5SDimitry Andric   if (!MKOpt)
9890b57cec5SDimitry Andric     return false;
9900b57cec5SDimitry Andric   NSAPI::NSNumberLiteralMethodKind MK = *MKOpt;
9910b57cec5SDimitry Andric 
9920b57cec5SDimitry Andric   const Expr *OrigArg = Arg->IgnoreImpCasts();
9930b57cec5SDimitry Andric   QualType FinalTy = Arg->getType();
9940b57cec5SDimitry Andric   QualType OrigTy = OrigArg->getType();
9950b57cec5SDimitry Andric   uint64_t FinalTySize = Ctx.getTypeSize(FinalTy);
9960b57cec5SDimitry Andric   uint64_t OrigTySize = Ctx.getTypeSize(OrigTy);
9970b57cec5SDimitry Andric 
9980b57cec5SDimitry Andric   bool isTruncated = FinalTySize < OrigTySize;
9990b57cec5SDimitry Andric   bool needsCast = false;
10000b57cec5SDimitry Andric 
10010b57cec5SDimitry Andric   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
10020b57cec5SDimitry Andric     switch (ICE->getCastKind()) {
10030b57cec5SDimitry Andric     case CK_LValueToRValue:
10040b57cec5SDimitry Andric     case CK_NoOp:
10050b57cec5SDimitry Andric     case CK_UserDefinedConversion:
10060b57cec5SDimitry Andric       break;
10070b57cec5SDimitry Andric 
10080b57cec5SDimitry Andric     case CK_IntegralCast: {
10090b57cec5SDimitry Andric       if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType())
10100b57cec5SDimitry Andric         break;
10110b57cec5SDimitry Andric       // Be more liberal with Integer/UnsignedInteger which are very commonly
10120b57cec5SDimitry Andric       // used.
10130b57cec5SDimitry Andric       if ((MK == NSAPI::NSNumberWithInteger ||
10140b57cec5SDimitry Andric            MK == NSAPI::NSNumberWithUnsignedInteger) &&
10150b57cec5SDimitry Andric           !isTruncated) {
10160b57cec5SDimitry Andric         if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg))
10170b57cec5SDimitry Andric           break;
10180b57cec5SDimitry Andric         if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() &&
10190b57cec5SDimitry Andric             OrigTySize >= Ctx.getTypeSize(Ctx.IntTy))
10200b57cec5SDimitry Andric           break;
10210b57cec5SDimitry Andric       }
10220b57cec5SDimitry Andric 
10230b57cec5SDimitry Andric       needsCast = true;
10240b57cec5SDimitry Andric       break;
10250b57cec5SDimitry Andric     }
10260b57cec5SDimitry Andric 
10270b57cec5SDimitry Andric     case CK_PointerToBoolean:
10280b57cec5SDimitry Andric     case CK_IntegralToBoolean:
10290b57cec5SDimitry Andric     case CK_IntegralToFloating:
10300b57cec5SDimitry Andric     case CK_FloatingToIntegral:
10310b57cec5SDimitry Andric     case CK_FloatingToBoolean:
10320b57cec5SDimitry Andric     case CK_FloatingCast:
10330b57cec5SDimitry Andric     case CK_FloatingComplexToReal:
10340b57cec5SDimitry Andric     case CK_FloatingComplexToBoolean:
10350b57cec5SDimitry Andric     case CK_IntegralComplexToReal:
10360b57cec5SDimitry Andric     case CK_IntegralComplexToBoolean:
10370b57cec5SDimitry Andric     case CK_AtomicToNonAtomic:
10380b57cec5SDimitry Andric     case CK_AddressSpaceConversion:
10390b57cec5SDimitry Andric       needsCast = true;
10400b57cec5SDimitry Andric       break;
10410b57cec5SDimitry Andric 
10420b57cec5SDimitry Andric     case CK_Dependent:
10430b57cec5SDimitry Andric     case CK_BitCast:
10440b57cec5SDimitry Andric     case CK_LValueBitCast:
10450b57cec5SDimitry Andric     case CK_LValueToRValueBitCast:
10460b57cec5SDimitry Andric     case CK_BaseToDerived:
10470b57cec5SDimitry Andric     case CK_DerivedToBase:
10480b57cec5SDimitry Andric     case CK_UncheckedDerivedToBase:
10490b57cec5SDimitry Andric     case CK_Dynamic:
10500b57cec5SDimitry Andric     case CK_ToUnion:
10510b57cec5SDimitry Andric     case CK_ArrayToPointerDecay:
10520b57cec5SDimitry Andric     case CK_FunctionToPointerDecay:
10530b57cec5SDimitry Andric     case CK_NullToPointer:
10540b57cec5SDimitry Andric     case CK_NullToMemberPointer:
10550b57cec5SDimitry Andric     case CK_BaseToDerivedMemberPointer:
10560b57cec5SDimitry Andric     case CK_DerivedToBaseMemberPointer:
10570b57cec5SDimitry Andric     case CK_MemberPointerToBoolean:
10580b57cec5SDimitry Andric     case CK_ReinterpretMemberPointer:
10590b57cec5SDimitry Andric     case CK_ConstructorConversion:
10600b57cec5SDimitry Andric     case CK_IntegralToPointer:
10610b57cec5SDimitry Andric     case CK_PointerToIntegral:
10620b57cec5SDimitry Andric     case CK_ToVoid:
10630b57cec5SDimitry Andric     case CK_VectorSplat:
10640b57cec5SDimitry Andric     case CK_CPointerToObjCPointerCast:
10650b57cec5SDimitry Andric     case CK_BlockPointerToObjCPointerCast:
10660b57cec5SDimitry Andric     case CK_AnyPointerToBlockPointerCast:
10670b57cec5SDimitry Andric     case CK_ObjCObjectLValueCast:
10680b57cec5SDimitry Andric     case CK_FloatingRealToComplex:
10690b57cec5SDimitry Andric     case CK_FloatingComplexCast:
10700b57cec5SDimitry Andric     case CK_FloatingComplexToIntegralComplex:
10710b57cec5SDimitry Andric     case CK_IntegralRealToComplex:
10720b57cec5SDimitry Andric     case CK_IntegralComplexCast:
10730b57cec5SDimitry Andric     case CK_IntegralComplexToFloatingComplex:
10740b57cec5SDimitry Andric     case CK_ARCProduceObject:
10750b57cec5SDimitry Andric     case CK_ARCConsumeObject:
10760b57cec5SDimitry Andric     case CK_ARCReclaimReturnedObject:
10770b57cec5SDimitry Andric     case CK_ARCExtendBlockObject:
10780b57cec5SDimitry Andric     case CK_NonAtomicToAtomic:
10790b57cec5SDimitry Andric     case CK_CopyAndAutoreleaseBlockObject:
10800b57cec5SDimitry Andric     case CK_BuiltinFnToFnPtr:
10810b57cec5SDimitry Andric     case CK_ZeroToOCLOpaqueType:
10820b57cec5SDimitry Andric     case CK_IntToOCLSampler:
1083*fe6060f1SDimitry Andric     case CK_MatrixCast:
10840b57cec5SDimitry Andric       return false;
10850b57cec5SDimitry Andric 
10860b57cec5SDimitry Andric     case CK_BooleanToSignedIntegral:
10870b57cec5SDimitry Andric       llvm_unreachable("OpenCL-specific cast in Objective-C?");
10880b57cec5SDimitry Andric 
1089e8d8bef9SDimitry Andric     case CK_FloatingToFixedPoint:
1090e8d8bef9SDimitry Andric     case CK_FixedPointToFloating:
10910b57cec5SDimitry Andric     case CK_FixedPointCast:
10920b57cec5SDimitry Andric     case CK_FixedPointToBoolean:
10930b57cec5SDimitry Andric     case CK_FixedPointToIntegral:
10940b57cec5SDimitry Andric     case CK_IntegralToFixedPoint:
10950b57cec5SDimitry Andric       llvm_unreachable("Fixed point types are disabled for Objective-C");
10960b57cec5SDimitry Andric     }
10970b57cec5SDimitry Andric   }
10980b57cec5SDimitry Andric 
10990b57cec5SDimitry Andric   if (needsCast) {
11000b57cec5SDimitry Andric     DiagnosticsEngine &Diags = Ctx.getDiagnostics();
11010b57cec5SDimitry Andric     // FIXME: Use a custom category name to distinguish migration diagnostics.
11020b57cec5SDimitry Andric     unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning,
11030b57cec5SDimitry Andric                        "converting to boxing syntax requires casting %0 to %1");
11040b57cec5SDimitry Andric     Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy
11050b57cec5SDimitry Andric         << Msg->getSourceRange();
11060b57cec5SDimitry Andric     return false;
11070b57cec5SDimitry Andric   }
11080b57cec5SDimitry Andric 
11090b57cec5SDimitry Andric   SourceRange ArgRange = OrigArg->getSourceRange();
11100b57cec5SDimitry Andric   commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
11110b57cec5SDimitry Andric 
11120b57cec5SDimitry Andric   if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
11130b57cec5SDimitry Andric     commit.insertBefore(ArgRange.getBegin(), "@");
11140b57cec5SDimitry Andric   else
11150b57cec5SDimitry Andric     commit.insertWrap("@(", ArgRange, ")");
11160b57cec5SDimitry Andric 
11170b57cec5SDimitry Andric   return true;
11180b57cec5SDimitry Andric }
11190b57cec5SDimitry Andric 
11200b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
11210b57cec5SDimitry Andric // rewriteToStringBoxedExpression.
11220b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
11230b57cec5SDimitry Andric 
11240b57cec5SDimitry Andric static bool doRewriteToUTF8StringBoxedExpressionHelper(
11250b57cec5SDimitry Andric                                               const ObjCMessageExpr *Msg,
11260b57cec5SDimitry Andric                                               const NSAPI &NS, Commit &commit) {
11270b57cec5SDimitry Andric   const Expr *Arg = Msg->getArg(0);
11280b57cec5SDimitry Andric   if (Arg->isTypeDependent())
11290b57cec5SDimitry Andric     return false;
11300b57cec5SDimitry Andric 
11310b57cec5SDimitry Andric   ASTContext &Ctx = NS.getASTContext();
11320b57cec5SDimitry Andric 
11330b57cec5SDimitry Andric   const Expr *OrigArg = Arg->IgnoreImpCasts();
11340b57cec5SDimitry Andric   QualType OrigTy = OrigArg->getType();
11350b57cec5SDimitry Andric   if (OrigTy->isArrayType())
11360b57cec5SDimitry Andric     OrigTy = Ctx.getArrayDecayedType(OrigTy);
11370b57cec5SDimitry Andric 
11380b57cec5SDimitry Andric   if (const StringLiteral *
11390b57cec5SDimitry Andric         StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) {
11400b57cec5SDimitry Andric     commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange());
11410b57cec5SDimitry Andric     commit.insert(StrE->getBeginLoc(), "@");
11420b57cec5SDimitry Andric     return true;
11430b57cec5SDimitry Andric   }
11440b57cec5SDimitry Andric 
11450b57cec5SDimitry Andric   if (const PointerType *PT = OrigTy->getAs<PointerType>()) {
11460b57cec5SDimitry Andric     QualType PointeeType = PT->getPointeeType();
11470b57cec5SDimitry Andric     if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) {
11480b57cec5SDimitry Andric       SourceRange ArgRange = OrigArg->getSourceRange();
11490b57cec5SDimitry Andric       commit.replaceWithInner(Msg->getSourceRange(), ArgRange);
11500b57cec5SDimitry Andric 
11510b57cec5SDimitry Andric       if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg))
11520b57cec5SDimitry Andric         commit.insertBefore(ArgRange.getBegin(), "@");
11530b57cec5SDimitry Andric       else
11540b57cec5SDimitry Andric         commit.insertWrap("@(", ArgRange, ")");
11550b57cec5SDimitry Andric 
11560b57cec5SDimitry Andric       return true;
11570b57cec5SDimitry Andric     }
11580b57cec5SDimitry Andric   }
11590b57cec5SDimitry Andric 
11600b57cec5SDimitry Andric   return false;
11610b57cec5SDimitry Andric }
11620b57cec5SDimitry Andric 
11630b57cec5SDimitry Andric static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg,
11640b57cec5SDimitry Andric                                            const NSAPI &NS, Commit &commit) {
11650b57cec5SDimitry Andric   Selector Sel = Msg->getSelector();
11660b57cec5SDimitry Andric 
11670b57cec5SDimitry Andric   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) ||
11680b57cec5SDimitry Andric       Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) ||
11690b57cec5SDimitry Andric       Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) {
11700b57cec5SDimitry Andric     if (Msg->getNumArgs() != 1)
11710b57cec5SDimitry Andric       return false;
11720b57cec5SDimitry Andric     return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
11730b57cec5SDimitry Andric   }
11740b57cec5SDimitry Andric 
11750b57cec5SDimitry Andric   if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) {
11760b57cec5SDimitry Andric     if (Msg->getNumArgs() != 2)
11770b57cec5SDimitry Andric       return false;
11780b57cec5SDimitry Andric 
11790b57cec5SDimitry Andric     const Expr *encodingArg = Msg->getArg(1);
11800b57cec5SDimitry Andric     if (NS.isNSUTF8StringEncodingConstant(encodingArg) ||
11810b57cec5SDimitry Andric         NS.isNSASCIIStringEncodingConstant(encodingArg))
11820b57cec5SDimitry Andric       return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit);
11830b57cec5SDimitry Andric   }
11840b57cec5SDimitry Andric 
11850b57cec5SDimitry Andric   return false;
11860b57cec5SDimitry Andric }
1187