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