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