RewriteObjCFoundationAPI.cpp revision 245431
1234287Sdim//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// Rewrites legacy method calls to modern syntax. 11234287Sdim// 12234287Sdim//===----------------------------------------------------------------------===// 13234287Sdim 14234287Sdim#include "clang/Edit/Rewriters.h" 15234287Sdim#include "clang/Edit/Commit.h" 16234287Sdim#include "clang/Lex/Lexer.h" 17245431Sdim#include "clang/AST/ASTContext.h" 18234287Sdim#include "clang/AST/ExprObjC.h" 19234287Sdim#include "clang/AST/ExprCXX.h" 20234287Sdim#include "clang/AST/NSAPI.h" 21234287Sdim 22234287Sdimusing namespace clang; 23234287Sdimusing namespace edit; 24234287Sdim 25234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg, 26245431Sdim IdentifierInfo *&ClassId, 27245431Sdim const LangOptions &LangOpts) { 28234287Sdim if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) 29234287Sdim return false; 30234287Sdim 31234287Sdim const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); 32234287Sdim if (!Receiver) 33234287Sdim return false; 34234287Sdim ClassId = Receiver->getIdentifier(); 35234287Sdim 36234287Sdim if (Msg->getReceiverKind() == ObjCMessageExpr::Class) 37234287Sdim return true; 38234287Sdim 39245431Sdim // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, 40245431Sdim // since the change from +1 to +0 will be handled fine by ARC. 41245431Sdim if (LangOpts.ObjCAutoRefCount) { 42245431Sdim if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { 43245431Sdim if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( 44245431Sdim Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { 45245431Sdim if (Rec->getMethodFamily() == OMF_alloc) 46245431Sdim return true; 47245431Sdim } 48245431Sdim } 49245431Sdim } 50245431Sdim 51234287Sdim return false; 52234287Sdim} 53234287Sdim 54234287Sdim//===----------------------------------------------------------------------===// 55234287Sdim// rewriteObjCRedundantCallWithLiteral. 56234287Sdim//===----------------------------------------------------------------------===// 57234287Sdim 58234287Sdimbool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, 59234287Sdim const NSAPI &NS, Commit &commit) { 60234287Sdim IdentifierInfo *II = 0; 61245431Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 62234287Sdim return false; 63234287Sdim if (Msg->getNumArgs() != 1) 64234287Sdim return false; 65234287Sdim 66234287Sdim const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 67234287Sdim Selector Sel = Msg->getSelector(); 68234287Sdim 69234287Sdim if ((isa<ObjCStringLiteral>(Arg) && 70234287Sdim NS.getNSClassId(NSAPI::ClassId_NSString) == II && 71245431Sdim (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || 72245431Sdim NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || 73234287Sdim 74234287Sdim (isa<ObjCArrayLiteral>(Arg) && 75234287Sdim NS.getNSClassId(NSAPI::ClassId_NSArray) == II && 76245431Sdim (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || 77245431Sdim NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || 78234287Sdim 79234287Sdim (isa<ObjCDictionaryLiteral>(Arg) && 80234287Sdim NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && 81245431Sdim (NS.getNSDictionarySelector( 82245431Sdim NSAPI::NSDict_dictionaryWithDictionary) == Sel || 83245431Sdim NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { 84234287Sdim 85234287Sdim commit.replaceWithInner(Msg->getSourceRange(), 86234287Sdim Msg->getArg(0)->getSourceRange()); 87234287Sdim return true; 88234287Sdim } 89234287Sdim 90234287Sdim return false; 91234287Sdim} 92234287Sdim 93234287Sdim//===----------------------------------------------------------------------===// 94234287Sdim// rewriteToObjCSubscriptSyntax. 95234287Sdim//===----------------------------------------------------------------------===// 96234287Sdim 97245431Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors 98245431Sdim/// that the migrator handles) but return their instances as 'id', resulting 99245431Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. 100245431Sdim/// 101245431Sdim/// When checking if we can convert to subscripting syntax, check whether 102245431Sdim/// the receiver is a result of a class method from a hardcoded list of 103245431Sdim/// such classes. In such a case return the specific class as the interface 104245431Sdim/// of the receiver. 105245431Sdim/// 106245431Sdim/// FIXME: Remove this when these classes start using 'instancetype'. 107245431Sdimstatic const ObjCInterfaceDecl * 108245431SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, 109245431Sdim const Expr *Receiver, 110245431Sdim ASTContext &Ctx) { 111245431Sdim assert(IFace && Receiver); 112245431Sdim 113245431Sdim // If the receiver has type 'id'... 114245431Sdim if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) 115245431Sdim return IFace; 116245431Sdim 117245431Sdim const ObjCMessageExpr * 118245431Sdim InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); 119245431Sdim if (!InnerMsg) 120245431Sdim return IFace; 121245431Sdim 122245431Sdim QualType ClassRec; 123245431Sdim switch (InnerMsg->getReceiverKind()) { 124245431Sdim case ObjCMessageExpr::Instance: 125245431Sdim case ObjCMessageExpr::SuperInstance: 126245431Sdim return IFace; 127245431Sdim 128245431Sdim case ObjCMessageExpr::Class: 129245431Sdim ClassRec = InnerMsg->getClassReceiver(); 130245431Sdim break; 131245431Sdim case ObjCMessageExpr::SuperClass: 132245431Sdim ClassRec = InnerMsg->getSuperType(); 133245431Sdim break; 134245431Sdim } 135245431Sdim 136245431Sdim if (ClassRec.isNull()) 137245431Sdim return IFace; 138245431Sdim 139245431Sdim // ...and it is the result of a class message... 140245431Sdim 141245431Sdim const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); 142245431Sdim if (!ObjTy) 143245431Sdim return IFace; 144245431Sdim const ObjCInterfaceDecl *OID = ObjTy->getInterface(); 145245431Sdim 146245431Sdim // ...and the receiving class is NSMapTable or NSLocale, return that 147245431Sdim // class as the receiving interface. 148245431Sdim if (OID->getName() == "NSMapTable" || 149245431Sdim OID->getName() == "NSLocale") 150245431Sdim return OID; 151245431Sdim 152245431Sdim return IFace; 153245431Sdim} 154245431Sdim 155245431Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, 156245431Sdim const ObjCMessageExpr *Msg, 157245431Sdim ASTContext &Ctx, 158245431Sdim Selector subscriptSel) { 159245431Sdim const Expr *Rec = Msg->getInstanceReceiver(); 160245431Sdim if (!Rec) 161245431Sdim return false; 162245431Sdim IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); 163245431Sdim 164245431Sdim if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { 165245431Sdim if (!MD->isUnavailable()) 166245431Sdim return true; 167245431Sdim } 168245431Sdim return false; 169245431Sdim} 170245431Sdim 171245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr); 172245431Sdim 173234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 174245431Sdim if (subscriptOperatorNeedsParens(Receiver)) { 175234287Sdim SourceRange RecRange = Receiver->getSourceRange(); 176234287Sdim commit.insertWrap("(", RecRange, ")"); 177234287Sdim } 178234287Sdim} 179234287Sdim 180245431Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 181245431Sdim Commit &commit) { 182234287Sdim if (Msg->getNumArgs() != 1) 183234287Sdim return false; 184234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 185234287Sdim if (!Rec) 186234287Sdim return false; 187234287Sdim 188234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 189234287Sdim SourceRange RecRange = Rec->getSourceRange(); 190234287Sdim SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 191234287Sdim 192234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 193234287Sdim ArgRange.getBegin()), 194234287Sdim CharSourceRange::getTokenRange(RecRange)); 195234287Sdim commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 196234287Sdim ArgRange); 197234287Sdim commit.insertWrap("[", ArgRange, "]"); 198234287Sdim maybePutParensOnReceiver(Rec, commit); 199234287Sdim return true; 200234287Sdim} 201234287Sdim 202245431Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 203245431Sdim const ObjCMessageExpr *Msg, 204245431Sdim const NSAPI &NS, 205234287Sdim Commit &commit) { 206245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 207245431Sdim NS.getObjectAtIndexedSubscriptSelector())) 208245431Sdim return false; 209245431Sdim return rewriteToSubscriptGetCommon(Msg, commit); 210245431Sdim} 211245431Sdim 212245431Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 213245431Sdim const ObjCMessageExpr *Msg, 214245431Sdim const NSAPI &NS, 215245431Sdim Commit &commit) { 216245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 217245431Sdim NS.getObjectForKeyedSubscriptSelector())) 218245431Sdim return false; 219245431Sdim return rewriteToSubscriptGetCommon(Msg, commit); 220245431Sdim} 221245431Sdim 222245431Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 223245431Sdim const ObjCMessageExpr *Msg, 224245431Sdim const NSAPI &NS, 225245431Sdim Commit &commit) { 226245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 227245431Sdim NS.getSetObjectAtIndexedSubscriptSelector())) 228245431Sdim return false; 229245431Sdim 230234287Sdim if (Msg->getNumArgs() != 2) 231234287Sdim return false; 232234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 233234287Sdim if (!Rec) 234234287Sdim return false; 235234287Sdim 236234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 237234287Sdim SourceRange RecRange = Rec->getSourceRange(); 238234287Sdim SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 239234287Sdim SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 240234287Sdim 241234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 242234287Sdim Arg0Range.getBegin()), 243234287Sdim CharSourceRange::getTokenRange(RecRange)); 244234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 245234287Sdim Arg1Range.getBegin()), 246234287Sdim CharSourceRange::getTokenRange(Arg0Range)); 247234287Sdim commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 248234287Sdim Arg1Range); 249234287Sdim commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 250234287Sdim Arg1Range.getBegin()), 251234287Sdim "] = "); 252234287Sdim maybePutParensOnReceiver(Rec, commit); 253234287Sdim return true; 254234287Sdim} 255234287Sdim 256245431Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 257245431Sdim const ObjCMessageExpr *Msg, 258245431Sdim const NSAPI &NS, 259234287Sdim Commit &commit) { 260245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 261245431Sdim NS.getSetObjectForKeyedSubscriptSelector())) 262245431Sdim return false; 263245431Sdim 264234287Sdim if (Msg->getNumArgs() != 2) 265234287Sdim return false; 266234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 267234287Sdim if (!Rec) 268234287Sdim return false; 269234287Sdim 270234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 271234287Sdim SourceRange RecRange = Rec->getSourceRange(); 272234287Sdim SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 273234287Sdim SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 274234287Sdim 275234287Sdim SourceLocation LocBeforeVal = Arg0Range.getBegin(); 276234287Sdim commit.insertBefore(LocBeforeVal, "] = "); 277234287Sdim commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 278234287Sdim /*beforePreviousInsertions=*/true); 279234287Sdim commit.insertBefore(LocBeforeVal, "["); 280234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 281234287Sdim Arg0Range.getBegin()), 282234287Sdim CharSourceRange::getTokenRange(RecRange)); 283234287Sdim commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 284234287Sdim Arg0Range); 285234287Sdim maybePutParensOnReceiver(Rec, commit); 286234287Sdim return true; 287234287Sdim} 288234287Sdim 289234287Sdimbool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 290245431Sdim const NSAPI &NS, Commit &commit) { 291234287Sdim if (!Msg || Msg->isImplicit() || 292234287Sdim Msg->getReceiverKind() != ObjCMessageExpr::Instance) 293234287Sdim return false; 294234287Sdim const ObjCMethodDecl *Method = Msg->getMethodDecl(); 295234287Sdim if (!Method) 296234287Sdim return false; 297234287Sdim 298234287Sdim const ObjCInterfaceDecl * 299234287Sdim IFace = NS.getASTContext().getObjContainingInterface( 300234287Sdim const_cast<ObjCMethodDecl *>(Method)); 301234287Sdim if (!IFace) 302234287Sdim return false; 303234287Sdim Selector Sel = Msg->getSelector(); 304234287Sdim 305245431Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 306245431Sdim return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 307234287Sdim 308245431Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 309245431Sdim return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 310245431Sdim 311234287Sdim if (Msg->getNumArgs() != 2) 312234287Sdim return false; 313234287Sdim 314245431Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 315245431Sdim return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 316234287Sdim 317245431Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 318245431Sdim return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 319234287Sdim 320234287Sdim return false; 321234287Sdim} 322234287Sdim 323234287Sdim//===----------------------------------------------------------------------===// 324234287Sdim// rewriteToObjCLiteralSyntax. 325234287Sdim//===----------------------------------------------------------------------===// 326234287Sdim 327234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 328234287Sdim const NSAPI &NS, Commit &commit); 329234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 330234287Sdim const NSAPI &NS, Commit &commit); 331234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 332234287Sdim const NSAPI &NS, Commit &commit); 333245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 334245431Sdim const NSAPI &NS, Commit &commit); 335245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 336245431Sdim const NSAPI &NS, Commit &commit); 337234287Sdim 338234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 339234287Sdim const NSAPI &NS, Commit &commit) { 340234287Sdim IdentifierInfo *II = 0; 341245431Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 342234287Sdim return false; 343234287Sdim 344234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 345234287Sdim return rewriteToArrayLiteral(Msg, NS, commit); 346234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 347234287Sdim return rewriteToDictionaryLiteral(Msg, NS, commit); 348234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 349234287Sdim return rewriteToNumberLiteral(Msg, NS, commit); 350245431Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 351245431Sdim return rewriteToStringBoxedExpression(Msg, NS, commit); 352234287Sdim 353234287Sdim return false; 354234287Sdim} 355234287Sdim 356234287Sdim//===----------------------------------------------------------------------===// 357234287Sdim// rewriteToArrayLiteral. 358234287Sdim//===----------------------------------------------------------------------===// 359234287Sdim 360245431Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object. 361245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit); 362245431Sdim 363234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 364234287Sdim const NSAPI &NS, Commit &commit) { 365234287Sdim Selector Sel = Msg->getSelector(); 366234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 367234287Sdim 368234287Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 369234287Sdim if (Msg->getNumArgs() != 0) 370234287Sdim return false; 371234287Sdim commit.replace(MsgRange, "@[]"); 372234287Sdim return true; 373234287Sdim } 374234287Sdim 375234287Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 376234287Sdim if (Msg->getNumArgs() != 1) 377234287Sdim return false; 378245431Sdim objectifyExpr(Msg->getArg(0), commit); 379234287Sdim SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 380234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 381234287Sdim commit.insertWrap("@[", ArgRange, "]"); 382234287Sdim return true; 383234287Sdim } 384234287Sdim 385245431Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 386245431Sdim Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 387234287Sdim if (Msg->getNumArgs() == 0) 388234287Sdim return false; 389234287Sdim const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 390234287Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 391234287Sdim return false; 392234287Sdim 393245431Sdim for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 394245431Sdim objectifyExpr(Msg->getArg(i), commit); 395245431Sdim 396234287Sdim if (Msg->getNumArgs() == 1) { 397234287Sdim commit.replace(MsgRange, "@[]"); 398234287Sdim return true; 399234287Sdim } 400234287Sdim SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 401234287Sdim Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 402234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 403234287Sdim commit.insertWrap("@[", ArgRange, "]"); 404234287Sdim return true; 405234287Sdim } 406234287Sdim 407234287Sdim return false; 408234287Sdim} 409234287Sdim 410234287Sdim//===----------------------------------------------------------------------===// 411234287Sdim// rewriteToDictionaryLiteral. 412234287Sdim//===----------------------------------------------------------------------===// 413234287Sdim 414234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 415234287Sdim const NSAPI &NS, Commit &commit) { 416234287Sdim Selector Sel = Msg->getSelector(); 417234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 418234287Sdim 419234287Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 420234287Sdim if (Msg->getNumArgs() != 0) 421234287Sdim return false; 422234287Sdim commit.replace(MsgRange, "@{}"); 423234287Sdim return true; 424234287Sdim } 425234287Sdim 426234287Sdim if (Sel == NS.getNSDictionarySelector( 427234287Sdim NSAPI::NSDict_dictionaryWithObjectForKey)) { 428234287Sdim if (Msg->getNumArgs() != 2) 429234287Sdim return false; 430245431Sdim 431245431Sdim objectifyExpr(Msg->getArg(0), commit); 432245431Sdim objectifyExpr(Msg->getArg(1), commit); 433245431Sdim 434234287Sdim SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 435234287Sdim SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 436234287Sdim // Insert key before the value. 437234287Sdim commit.insertBefore(ValRange.getBegin(), ": "); 438234287Sdim commit.insertFromRange(ValRange.getBegin(), 439234287Sdim CharSourceRange::getTokenRange(KeyRange), 440234287Sdim /*afterToken=*/false, /*beforePreviousInsertions=*/true); 441234287Sdim commit.insertBefore(ValRange.getBegin(), "@{"); 442234287Sdim commit.insertAfterToken(ValRange.getEnd(), "}"); 443234287Sdim commit.replaceWithInner(MsgRange, ValRange); 444234287Sdim return true; 445234287Sdim } 446234287Sdim 447234287Sdim if (Sel == NS.getNSDictionarySelector( 448245431Sdim NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 449245431Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 450234287Sdim if (Msg->getNumArgs() % 2 != 1) 451234287Sdim return false; 452234287Sdim unsigned SentinelIdx = Msg->getNumArgs() - 1; 453234287Sdim const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 454234287Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 455234287Sdim return false; 456234287Sdim 457234287Sdim if (Msg->getNumArgs() == 1) { 458234287Sdim commit.replace(MsgRange, "@{}"); 459234287Sdim return true; 460234287Sdim } 461234287Sdim 462234287Sdim for (unsigned i = 0; i < SentinelIdx; i += 2) { 463245431Sdim objectifyExpr(Msg->getArg(i), commit); 464245431Sdim objectifyExpr(Msg->getArg(i+1), commit); 465245431Sdim 466234287Sdim SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 467234287Sdim SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 468234287Sdim // Insert value after key. 469234287Sdim commit.insertAfterToken(KeyRange.getEnd(), ": "); 470234287Sdim commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 471234287Sdim commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 472234287Sdim KeyRange.getBegin())); 473234287Sdim } 474234287Sdim // Range of arguments up until and including the last key. 475234287Sdim // The sentinel and first value are cut off, the value will move after the 476234287Sdim // key. 477234287Sdim SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 478234287Sdim Msg->getArg(SentinelIdx-1)->getLocEnd()); 479234287Sdim commit.insertWrap("@{", ArgRange, "}"); 480234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 481234287Sdim return true; 482234287Sdim } 483234287Sdim 484234287Sdim return false; 485234287Sdim} 486234287Sdim 487234287Sdim//===----------------------------------------------------------------------===// 488234287Sdim// rewriteToNumberLiteral. 489234287Sdim//===----------------------------------------------------------------------===// 490234287Sdim 491234287Sdimstatic bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 492234287Sdim const CharacterLiteral *Arg, 493234287Sdim const NSAPI &NS, Commit &commit) { 494234287Sdim if (Arg->getKind() != CharacterLiteral::Ascii) 495234287Sdim return false; 496234287Sdim if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 497234287Sdim Msg->getSelector())) { 498234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 499234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 500234287Sdim commit.insert(ArgRange.getBegin(), "@"); 501234287Sdim return true; 502234287Sdim } 503234287Sdim 504245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 505234287Sdim} 506234287Sdim 507234287Sdimstatic bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 508234287Sdim const Expr *Arg, 509234287Sdim const NSAPI &NS, Commit &commit) { 510234287Sdim if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 511234287Sdim Msg->getSelector())) { 512234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 513234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 514234287Sdim commit.insert(ArgRange.getBegin(), "@"); 515234287Sdim return true; 516234287Sdim } 517234287Sdim 518245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 519234287Sdim} 520234287Sdim 521234287Sdimnamespace { 522234287Sdim 523234287Sdimstruct LiteralInfo { 524234287Sdim bool Hex, Octal; 525234287Sdim StringRef U, F, L, LL; 526234287Sdim CharSourceRange WithoutSuffRange; 527234287Sdim}; 528234287Sdim 529234287Sdim} 530234287Sdim 531234287Sdimstatic bool getLiteralInfo(SourceRange literalRange, 532234287Sdim bool isFloat, bool isIntZero, 533234287Sdim ASTContext &Ctx, LiteralInfo &Info) { 534234287Sdim if (literalRange.getBegin().isMacroID() || 535234287Sdim literalRange.getEnd().isMacroID()) 536234287Sdim return false; 537234287Sdim StringRef text = Lexer::getSourceText( 538234287Sdim CharSourceRange::getTokenRange(literalRange), 539234287Sdim Ctx.getSourceManager(), Ctx.getLangOpts()); 540234287Sdim if (text.empty()) 541234287Sdim return false; 542234287Sdim 543234287Sdim llvm::Optional<bool> UpperU, UpperL; 544234287Sdim bool UpperF = false; 545234287Sdim 546234287Sdim struct Suff { 547234287Sdim static bool has(StringRef suff, StringRef &text) { 548234287Sdim if (text.endswith(suff)) { 549234287Sdim text = text.substr(0, text.size()-suff.size()); 550234287Sdim return true; 551234287Sdim } 552234287Sdim return false; 553234287Sdim } 554234287Sdim }; 555234287Sdim 556234287Sdim while (1) { 557234287Sdim if (Suff::has("u", text)) { 558234287Sdim UpperU = false; 559234287Sdim } else if (Suff::has("U", text)) { 560234287Sdim UpperU = true; 561234287Sdim } else if (Suff::has("ll", text)) { 562234287Sdim UpperL = false; 563234287Sdim } else if (Suff::has("LL", text)) { 564234287Sdim UpperL = true; 565234287Sdim } else if (Suff::has("l", text)) { 566234287Sdim UpperL = false; 567234287Sdim } else if (Suff::has("L", text)) { 568234287Sdim UpperL = true; 569234287Sdim } else if (isFloat && Suff::has("f", text)) { 570234287Sdim UpperF = false; 571234287Sdim } else if (isFloat && Suff::has("F", text)) { 572234287Sdim UpperF = true; 573234287Sdim } else 574234287Sdim break; 575234287Sdim } 576234287Sdim 577234287Sdim if (!UpperU.hasValue() && !UpperL.hasValue()) 578234287Sdim UpperU = UpperL = true; 579234287Sdim else if (UpperU.hasValue() && !UpperL.hasValue()) 580234287Sdim UpperL = UpperU; 581234287Sdim else if (UpperL.hasValue() && !UpperU.hasValue()) 582234287Sdim UpperU = UpperL; 583234287Sdim 584234287Sdim Info.U = *UpperU ? "U" : "u"; 585234287Sdim Info.L = *UpperL ? "L" : "l"; 586234287Sdim Info.LL = *UpperL ? "LL" : "ll"; 587234287Sdim Info.F = UpperF ? "F" : "f"; 588234287Sdim 589234287Sdim Info.Hex = Info.Octal = false; 590234287Sdim if (text.startswith("0x")) 591234287Sdim Info.Hex = true; 592234287Sdim else if (!isFloat && !isIntZero && text.startswith("0")) 593234287Sdim Info.Octal = true; 594234287Sdim 595234287Sdim SourceLocation B = literalRange.getBegin(); 596234287Sdim Info.WithoutSuffRange = 597234287Sdim CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 598234287Sdim return true; 599234287Sdim} 600234287Sdim 601234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 602234287Sdim const NSAPI &NS, Commit &commit) { 603234287Sdim if (Msg->getNumArgs() != 1) 604234287Sdim return false; 605234287Sdim 606234287Sdim const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 607234287Sdim if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 608234287Sdim return rewriteToCharLiteral(Msg, CharE, NS, commit); 609234287Sdim if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 610234287Sdim return rewriteToBoolLiteral(Msg, BE, NS, commit); 611234287Sdim if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 612234287Sdim return rewriteToBoolLiteral(Msg, BE, NS, commit); 613234287Sdim 614234287Sdim const Expr *literalE = Arg; 615234287Sdim if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 616234287Sdim if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 617234287Sdim literalE = UOE->getSubExpr(); 618234287Sdim } 619234287Sdim 620245431Sdim // Only integer and floating literals, otherwise try to rewrite to boxed 621245431Sdim // expression. 622234287Sdim if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 623245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 624234287Sdim 625234287Sdim ASTContext &Ctx = NS.getASTContext(); 626234287Sdim Selector Sel = Msg->getSelector(); 627234287Sdim llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 628234287Sdim MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 629234287Sdim if (!MKOpt) 630234287Sdim return false; 631234287Sdim NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 632234287Sdim 633234287Sdim bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 634234287Sdim bool CallIsFloating = false, CallIsDouble = false; 635234287Sdim 636234287Sdim switch (MK) { 637234287Sdim // We cannot have these calls with int/float literals. 638234287Sdim case NSAPI::NSNumberWithChar: 639234287Sdim case NSAPI::NSNumberWithUnsignedChar: 640234287Sdim case NSAPI::NSNumberWithShort: 641234287Sdim case NSAPI::NSNumberWithUnsignedShort: 642234287Sdim case NSAPI::NSNumberWithBool: 643245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 644234287Sdim 645234287Sdim case NSAPI::NSNumberWithUnsignedInt: 646234287Sdim case NSAPI::NSNumberWithUnsignedInteger: 647234287Sdim CallIsUnsigned = true; 648234287Sdim case NSAPI::NSNumberWithInt: 649234287Sdim case NSAPI::NSNumberWithInteger: 650234287Sdim break; 651234287Sdim 652234287Sdim case NSAPI::NSNumberWithUnsignedLong: 653234287Sdim CallIsUnsigned = true; 654234287Sdim case NSAPI::NSNumberWithLong: 655234287Sdim CallIsLong = true; 656234287Sdim break; 657234287Sdim 658234287Sdim case NSAPI::NSNumberWithUnsignedLongLong: 659234287Sdim CallIsUnsigned = true; 660234287Sdim case NSAPI::NSNumberWithLongLong: 661234287Sdim CallIsLongLong = true; 662234287Sdim break; 663234287Sdim 664234287Sdim case NSAPI::NSNumberWithDouble: 665234287Sdim CallIsDouble = true; 666234287Sdim case NSAPI::NSNumberWithFloat: 667234287Sdim CallIsFloating = true; 668234287Sdim break; 669234287Sdim } 670234287Sdim 671234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 672234287Sdim QualType ArgTy = Arg->getType(); 673234287Sdim QualType CallTy = Msg->getArg(0)->getType(); 674234287Sdim 675234287Sdim // Check for the easy case, the literal maps directly to the call. 676234287Sdim if (Ctx.hasSameType(ArgTy, CallTy)) { 677234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 678234287Sdim commit.insert(ArgRange.getBegin(), "@"); 679234287Sdim return true; 680234287Sdim } 681234287Sdim 682234287Sdim // We will need to modify the literal suffix to get the same type as the call. 683245431Sdim // Try with boxed expression if it came from a macro. 684234287Sdim if (ArgRange.getBegin().isMacroID()) 685245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 686234287Sdim 687234287Sdim bool LitIsFloat = ArgTy->isFloatingType(); 688245431Sdim // For a float passed to integer call, don't try rewriting to objc literal. 689245431Sdim // It is difficult and a very uncommon case anyway. 690245431Sdim // But try with boxed expression. 691234287Sdim if (LitIsFloat && !CallIsFloating) 692245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 693234287Sdim 694234287Sdim // Try to modify the literal make it the same type as the method call. 695234287Sdim // -Modify the suffix, and/or 696234287Sdim // -Change integer to float 697234287Sdim 698234287Sdim LiteralInfo LitInfo; 699234287Sdim bool isIntZero = false; 700234287Sdim if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 701234287Sdim isIntZero = !IntE->getValue().getBoolValue(); 702234287Sdim if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 703245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 704234287Sdim 705234287Sdim // Not easy to do int -> float with hex/octal and uncommon anyway. 706234287Sdim if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 707245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 708234287Sdim 709234287Sdim SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 710234287Sdim SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 711234287Sdim 712234287Sdim commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 713234287Sdim LitInfo.WithoutSuffRange); 714234287Sdim commit.insert(LitB, "@"); 715234287Sdim 716234287Sdim if (!LitIsFloat && CallIsFloating) 717234287Sdim commit.insert(LitE, ".0"); 718234287Sdim 719234287Sdim if (CallIsFloating) { 720234287Sdim if (!CallIsDouble) 721234287Sdim commit.insert(LitE, LitInfo.F); 722234287Sdim } else { 723234287Sdim if (CallIsUnsigned) 724234287Sdim commit.insert(LitE, LitInfo.U); 725234287Sdim 726234287Sdim if (CallIsLong) 727234287Sdim commit.insert(LitE, LitInfo.L); 728234287Sdim else if (CallIsLongLong) 729234287Sdim commit.insert(LitE, LitInfo.LL); 730234287Sdim } 731234287Sdim return true; 732234287Sdim} 733245431Sdim 734245431Sdim// FIXME: Make determination of operator precedence more general and 735245431Sdim// make it broadly available. 736245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 737245431Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 738245431Sdim if (isa<ArraySubscriptExpr>(Expr) || 739245431Sdim isa<CallExpr>(Expr) || 740245431Sdim isa<DeclRefExpr>(Expr) || 741245431Sdim isa<CXXNamedCastExpr>(Expr) || 742245431Sdim isa<CXXConstructExpr>(Expr) || 743245431Sdim isa<CXXThisExpr>(Expr) || 744245431Sdim isa<CXXTypeidExpr>(Expr) || 745245431Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 746245431Sdim isa<ObjCMessageExpr>(Expr) || 747245431Sdim isa<ObjCPropertyRefExpr>(Expr) || 748245431Sdim isa<ObjCProtocolExpr>(Expr) || 749245431Sdim isa<MemberExpr>(Expr) || 750245431Sdim isa<ObjCIvarRefExpr>(Expr) || 751245431Sdim isa<ParenExpr>(FullExpr) || 752245431Sdim isa<ParenListExpr>(Expr) || 753245431Sdim isa<SizeOfPackExpr>(Expr)) 754245431Sdim return false; 755245431Sdim 756245431Sdim return true; 757245431Sdim} 758245431Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) { 759245431Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 760245431Sdim if (isa<ArraySubscriptExpr>(Expr) || 761245431Sdim isa<CallExpr>(Expr) || 762245431Sdim isa<DeclRefExpr>(Expr) || 763245431Sdim isa<CastExpr>(Expr) || 764245431Sdim isa<CXXNewExpr>(Expr) || 765245431Sdim isa<CXXConstructExpr>(Expr) || 766245431Sdim isa<CXXDeleteExpr>(Expr) || 767245431Sdim isa<CXXNoexceptExpr>(Expr) || 768245431Sdim isa<CXXPseudoDestructorExpr>(Expr) || 769245431Sdim isa<CXXScalarValueInitExpr>(Expr) || 770245431Sdim isa<CXXThisExpr>(Expr) || 771245431Sdim isa<CXXTypeidExpr>(Expr) || 772245431Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 773245431Sdim isa<ObjCMessageExpr>(Expr) || 774245431Sdim isa<ObjCPropertyRefExpr>(Expr) || 775245431Sdim isa<ObjCProtocolExpr>(Expr) || 776245431Sdim isa<MemberExpr>(Expr) || 777245431Sdim isa<ObjCIvarRefExpr>(Expr) || 778245431Sdim isa<ParenExpr>(FullExpr) || 779245431Sdim isa<ParenListExpr>(Expr) || 780245431Sdim isa<SizeOfPackExpr>(Expr) || 781245431Sdim isa<UnaryOperator>(Expr)) 782245431Sdim return false; 783245431Sdim 784245431Sdim return true; 785245431Sdim} 786245431Sdim 787245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) { 788245431Sdim if (!E) return; 789245431Sdim 790245431Sdim QualType T = E->getType(); 791245431Sdim if (T->isObjCObjectPointerType()) { 792245431Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 793245431Sdim if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 794245431Sdim return; 795245431Sdim } else { 796245431Sdim return; 797245431Sdim } 798245431Sdim } else if (!T->isPointerType()) { 799245431Sdim return; 800245431Sdim } 801245431Sdim 802245431Sdim SourceRange Range = E->getSourceRange(); 803245431Sdim if (castOperatorNeedsParens(E)) 804245431Sdim commit.insertWrap("(", Range, ")"); 805245431Sdim commit.insertBefore(Range.getBegin(), "(id)"); 806245431Sdim} 807245431Sdim 808245431Sdim//===----------------------------------------------------------------------===// 809245431Sdim// rewriteToNumericBoxedExpression. 810245431Sdim//===----------------------------------------------------------------------===// 811245431Sdim 812245431Sdimstatic bool isEnumConstant(const Expr *E) { 813245431Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 814245431Sdim if (const ValueDecl *VD = DRE->getDecl()) 815245431Sdim return isa<EnumConstantDecl>(VD); 816245431Sdim 817245431Sdim return false; 818245431Sdim} 819245431Sdim 820245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 821245431Sdim const NSAPI &NS, Commit &commit) { 822245431Sdim if (Msg->getNumArgs() != 1) 823245431Sdim return false; 824245431Sdim 825245431Sdim const Expr *Arg = Msg->getArg(0); 826245431Sdim if (Arg->isTypeDependent()) 827245431Sdim return false; 828245431Sdim 829245431Sdim ASTContext &Ctx = NS.getASTContext(); 830245431Sdim Selector Sel = Msg->getSelector(); 831245431Sdim llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 832245431Sdim MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 833245431Sdim if (!MKOpt) 834245431Sdim return false; 835245431Sdim NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 836245431Sdim 837245431Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 838245431Sdim QualType FinalTy = Arg->getType(); 839245431Sdim QualType OrigTy = OrigArg->getType(); 840245431Sdim uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 841245431Sdim uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 842245431Sdim 843245431Sdim bool isTruncated = FinalTySize < OrigTySize; 844245431Sdim bool needsCast = false; 845245431Sdim 846245431Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 847245431Sdim switch (ICE->getCastKind()) { 848245431Sdim case CK_LValueToRValue: 849245431Sdim case CK_NoOp: 850245431Sdim case CK_UserDefinedConversion: 851245431Sdim break; 852245431Sdim 853245431Sdim case CK_IntegralCast: { 854245431Sdim if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 855245431Sdim break; 856245431Sdim // Be more liberal with Integer/UnsignedInteger which are very commonly 857245431Sdim // used. 858245431Sdim if ((MK == NSAPI::NSNumberWithInteger || 859245431Sdim MK == NSAPI::NSNumberWithUnsignedInteger) && 860245431Sdim !isTruncated) { 861245431Sdim if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 862245431Sdim break; 863245431Sdim if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 864245431Sdim OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 865245431Sdim break; 866245431Sdim } 867245431Sdim 868245431Sdim needsCast = true; 869245431Sdim break; 870245431Sdim } 871245431Sdim 872245431Sdim case CK_PointerToBoolean: 873245431Sdim case CK_IntegralToBoolean: 874245431Sdim case CK_IntegralToFloating: 875245431Sdim case CK_FloatingToIntegral: 876245431Sdim case CK_FloatingToBoolean: 877245431Sdim case CK_FloatingCast: 878245431Sdim case CK_FloatingComplexToReal: 879245431Sdim case CK_FloatingComplexToBoolean: 880245431Sdim case CK_IntegralComplexToReal: 881245431Sdim case CK_IntegralComplexToBoolean: 882245431Sdim case CK_AtomicToNonAtomic: 883245431Sdim needsCast = true; 884245431Sdim break; 885245431Sdim 886245431Sdim case CK_Dependent: 887245431Sdim case CK_BitCast: 888245431Sdim case CK_LValueBitCast: 889245431Sdim case CK_BaseToDerived: 890245431Sdim case CK_DerivedToBase: 891245431Sdim case CK_UncheckedDerivedToBase: 892245431Sdim case CK_Dynamic: 893245431Sdim case CK_ToUnion: 894245431Sdim case CK_ArrayToPointerDecay: 895245431Sdim case CK_FunctionToPointerDecay: 896245431Sdim case CK_NullToPointer: 897245431Sdim case CK_NullToMemberPointer: 898245431Sdim case CK_BaseToDerivedMemberPointer: 899245431Sdim case CK_DerivedToBaseMemberPointer: 900245431Sdim case CK_MemberPointerToBoolean: 901245431Sdim case CK_ReinterpretMemberPointer: 902245431Sdim case CK_ConstructorConversion: 903245431Sdim case CK_IntegralToPointer: 904245431Sdim case CK_PointerToIntegral: 905245431Sdim case CK_ToVoid: 906245431Sdim case CK_VectorSplat: 907245431Sdim case CK_CPointerToObjCPointerCast: 908245431Sdim case CK_BlockPointerToObjCPointerCast: 909245431Sdim case CK_AnyPointerToBlockPointerCast: 910245431Sdim case CK_ObjCObjectLValueCast: 911245431Sdim case CK_FloatingRealToComplex: 912245431Sdim case CK_FloatingComplexCast: 913245431Sdim case CK_FloatingComplexToIntegralComplex: 914245431Sdim case CK_IntegralRealToComplex: 915245431Sdim case CK_IntegralComplexCast: 916245431Sdim case CK_IntegralComplexToFloatingComplex: 917245431Sdim case CK_ARCProduceObject: 918245431Sdim case CK_ARCConsumeObject: 919245431Sdim case CK_ARCReclaimReturnedObject: 920245431Sdim case CK_ARCExtendBlockObject: 921245431Sdim case CK_NonAtomicToAtomic: 922245431Sdim case CK_CopyAndAutoreleaseBlockObject: 923245431Sdim case CK_BuiltinFnToFnPtr: 924245431Sdim return false; 925245431Sdim } 926245431Sdim } 927245431Sdim 928245431Sdim if (needsCast) { 929245431Sdim DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 930245431Sdim // FIXME: Use a custom category name to distinguish migration diagnostics. 931245431Sdim unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 932245431Sdim "converting to boxing syntax requires casting %0 to %1"); 933245431Sdim Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 934245431Sdim << Msg->getSourceRange(); 935245431Sdim return false; 936245431Sdim } 937245431Sdim 938245431Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 939245431Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 940245431Sdim 941245431Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 942245431Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 943245431Sdim else 944245431Sdim commit.insertWrap("@(", ArgRange, ")"); 945245431Sdim 946245431Sdim return true; 947245431Sdim} 948245431Sdim 949245431Sdim//===----------------------------------------------------------------------===// 950245431Sdim// rewriteToStringBoxedExpression. 951245431Sdim//===----------------------------------------------------------------------===// 952245431Sdim 953245431Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper( 954245431Sdim const ObjCMessageExpr *Msg, 955245431Sdim const NSAPI &NS, Commit &commit) { 956245431Sdim const Expr *Arg = Msg->getArg(0); 957245431Sdim if (Arg->isTypeDependent()) 958245431Sdim return false; 959245431Sdim 960245431Sdim ASTContext &Ctx = NS.getASTContext(); 961245431Sdim 962245431Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 963245431Sdim QualType OrigTy = OrigArg->getType(); 964245431Sdim if (OrigTy->isArrayType()) 965245431Sdim OrigTy = Ctx.getArrayDecayedType(OrigTy); 966245431Sdim 967245431Sdim if (const StringLiteral * 968245431Sdim StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 969245431Sdim commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 970245431Sdim commit.insert(StrE->getLocStart(), "@"); 971245431Sdim return true; 972245431Sdim } 973245431Sdim 974245431Sdim if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 975245431Sdim QualType PointeeType = PT->getPointeeType(); 976245431Sdim if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 977245431Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 978245431Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 979245431Sdim 980245431Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 981245431Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 982245431Sdim else 983245431Sdim commit.insertWrap("@(", ArgRange, ")"); 984245431Sdim 985245431Sdim return true; 986245431Sdim } 987245431Sdim } 988245431Sdim 989245431Sdim return false; 990245431Sdim} 991245431Sdim 992245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 993245431Sdim const NSAPI &NS, Commit &commit) { 994245431Sdim Selector Sel = Msg->getSelector(); 995245431Sdim 996245431Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 997245431Sdim Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 998245431Sdim if (Msg->getNumArgs() != 1) 999245431Sdim return false; 1000245431Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1001245431Sdim } 1002245431Sdim 1003245431Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1004245431Sdim if (Msg->getNumArgs() != 2) 1005245431Sdim return false; 1006245431Sdim 1007245431Sdim const Expr *encodingArg = Msg->getArg(1); 1008245431Sdim if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1009245431Sdim NS.isNSASCIIStringEncodingConstant(encodingArg)) 1010245431Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1011245431Sdim } 1012245431Sdim 1013245431Sdim return false; 1014245431Sdim} 1015