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: 10830b57cec5SDimitry Andric return false; 10840b57cec5SDimitry Andric 10850b57cec5SDimitry Andric case CK_BooleanToSignedIntegral: 10860b57cec5SDimitry Andric llvm_unreachable("OpenCL-specific cast in Objective-C?"); 10870b57cec5SDimitry Andric 1088*e8d8bef9SDimitry Andric case CK_FloatingToFixedPoint: 1089*e8d8bef9SDimitry Andric case CK_FixedPointToFloating: 10900b57cec5SDimitry Andric case CK_FixedPointCast: 10910b57cec5SDimitry Andric case CK_FixedPointToBoolean: 10920b57cec5SDimitry Andric case CK_FixedPointToIntegral: 10930b57cec5SDimitry Andric case CK_IntegralToFixedPoint: 10940b57cec5SDimitry Andric llvm_unreachable("Fixed point types are disabled for Objective-C"); 10950b57cec5SDimitry Andric } 10960b57cec5SDimitry Andric } 10970b57cec5SDimitry Andric 10980b57cec5SDimitry Andric if (needsCast) { 10990b57cec5SDimitry Andric DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 11000b57cec5SDimitry Andric // FIXME: Use a custom category name to distinguish migration diagnostics. 11010b57cec5SDimitry Andric unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 11020b57cec5SDimitry Andric "converting to boxing syntax requires casting %0 to %1"); 11030b57cec5SDimitry Andric Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 11040b57cec5SDimitry Andric << Msg->getSourceRange(); 11050b57cec5SDimitry Andric return false; 11060b57cec5SDimitry Andric } 11070b57cec5SDimitry Andric 11080b57cec5SDimitry Andric SourceRange ArgRange = OrigArg->getSourceRange(); 11090b57cec5SDimitry Andric commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 11100b57cec5SDimitry Andric 11110b57cec5SDimitry Andric if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 11120b57cec5SDimitry Andric commit.insertBefore(ArgRange.getBegin(), "@"); 11130b57cec5SDimitry Andric else 11140b57cec5SDimitry Andric commit.insertWrap("@(", ArgRange, ")"); 11150b57cec5SDimitry Andric 11160b57cec5SDimitry Andric return true; 11170b57cec5SDimitry Andric } 11180b57cec5SDimitry Andric 11190b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 11200b57cec5SDimitry Andric // rewriteToStringBoxedExpression. 11210b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 11220b57cec5SDimitry Andric 11230b57cec5SDimitry Andric static bool doRewriteToUTF8StringBoxedExpressionHelper( 11240b57cec5SDimitry Andric const ObjCMessageExpr *Msg, 11250b57cec5SDimitry Andric const NSAPI &NS, Commit &commit) { 11260b57cec5SDimitry Andric const Expr *Arg = Msg->getArg(0); 11270b57cec5SDimitry Andric if (Arg->isTypeDependent()) 11280b57cec5SDimitry Andric return false; 11290b57cec5SDimitry Andric 11300b57cec5SDimitry Andric ASTContext &Ctx = NS.getASTContext(); 11310b57cec5SDimitry Andric 11320b57cec5SDimitry Andric const Expr *OrigArg = Arg->IgnoreImpCasts(); 11330b57cec5SDimitry Andric QualType OrigTy = OrigArg->getType(); 11340b57cec5SDimitry Andric if (OrigTy->isArrayType()) 11350b57cec5SDimitry Andric OrigTy = Ctx.getArrayDecayedType(OrigTy); 11360b57cec5SDimitry Andric 11370b57cec5SDimitry Andric if (const StringLiteral * 11380b57cec5SDimitry Andric StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 11390b57cec5SDimitry Andric commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 11400b57cec5SDimitry Andric commit.insert(StrE->getBeginLoc(), "@"); 11410b57cec5SDimitry Andric return true; 11420b57cec5SDimitry Andric } 11430b57cec5SDimitry Andric 11440b57cec5SDimitry Andric if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 11450b57cec5SDimitry Andric QualType PointeeType = PT->getPointeeType(); 11460b57cec5SDimitry Andric if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 11470b57cec5SDimitry Andric SourceRange ArgRange = OrigArg->getSourceRange(); 11480b57cec5SDimitry Andric commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 11490b57cec5SDimitry Andric 11500b57cec5SDimitry Andric if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 11510b57cec5SDimitry Andric commit.insertBefore(ArgRange.getBegin(), "@"); 11520b57cec5SDimitry Andric else 11530b57cec5SDimitry Andric commit.insertWrap("@(", ArgRange, ")"); 11540b57cec5SDimitry Andric 11550b57cec5SDimitry Andric return true; 11560b57cec5SDimitry Andric } 11570b57cec5SDimitry Andric } 11580b57cec5SDimitry Andric 11590b57cec5SDimitry Andric return false; 11600b57cec5SDimitry Andric } 11610b57cec5SDimitry Andric 11620b57cec5SDimitry Andric static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 11630b57cec5SDimitry Andric const NSAPI &NS, Commit &commit) { 11640b57cec5SDimitry Andric Selector Sel = Msg->getSelector(); 11650b57cec5SDimitry Andric 11660b57cec5SDimitry Andric if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 11670b57cec5SDimitry Andric Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) || 11680b57cec5SDimitry Andric Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) { 11690b57cec5SDimitry Andric if (Msg->getNumArgs() != 1) 11700b57cec5SDimitry Andric return false; 11710b57cec5SDimitry Andric return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 11720b57cec5SDimitry Andric } 11730b57cec5SDimitry Andric 11740b57cec5SDimitry Andric if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 11750b57cec5SDimitry Andric if (Msg->getNumArgs() != 2) 11760b57cec5SDimitry Andric return false; 11770b57cec5SDimitry Andric 11780b57cec5SDimitry Andric const Expr *encodingArg = Msg->getArg(1); 11790b57cec5SDimitry Andric if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 11800b57cec5SDimitry Andric NS.isNSASCIIStringEncodingConstant(encodingArg)) 11810b57cec5SDimitry Andric return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 11820b57cec5SDimitry Andric } 11830b57cec5SDimitry Andric 11840b57cec5SDimitry Andric return false; 11850b57cec5SDimitry Andric } 1186