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