RewriteObjCFoundationAPI.cpp revision 252723
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" 15245431Sdim#include "clang/AST/ASTContext.h" 16252723Sdim#include "clang/AST/ExprCXX.h" 17234287Sdim#include "clang/AST/ExprObjC.h" 18234287Sdim#include "clang/AST/NSAPI.h" 19252723Sdim#include "clang/AST/ParentMap.h" 20252723Sdim#include "clang/Edit/Commit.h" 21252723Sdim#include "clang/Lex/Lexer.h" 22234287Sdim 23234287Sdimusing namespace clang; 24234287Sdimusing namespace edit; 25234287Sdim 26234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg, 27245431Sdim IdentifierInfo *&ClassId, 28245431Sdim const LangOptions &LangOpts) { 29234287Sdim if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) 30234287Sdim return false; 31234287Sdim 32234287Sdim const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); 33234287Sdim if (!Receiver) 34234287Sdim return false; 35234287Sdim ClassId = Receiver->getIdentifier(); 36234287Sdim 37234287Sdim if (Msg->getReceiverKind() == ObjCMessageExpr::Class) 38234287Sdim return true; 39234287Sdim 40245431Sdim // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, 41245431Sdim // since the change from +1 to +0 will be handled fine by ARC. 42245431Sdim if (LangOpts.ObjCAutoRefCount) { 43245431Sdim if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { 44245431Sdim if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( 45245431Sdim Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { 46245431Sdim if (Rec->getMethodFamily() == OMF_alloc) 47245431Sdim return true; 48245431Sdim } 49245431Sdim } 50245431Sdim } 51245431Sdim 52234287Sdim return false; 53234287Sdim} 54234287Sdim 55234287Sdim//===----------------------------------------------------------------------===// 56234287Sdim// rewriteObjCRedundantCallWithLiteral. 57234287Sdim//===----------------------------------------------------------------------===// 58234287Sdim 59234287Sdimbool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, 60234287Sdim const NSAPI &NS, Commit &commit) { 61234287Sdim IdentifierInfo *II = 0; 62245431Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 63234287Sdim return false; 64234287Sdim if (Msg->getNumArgs() != 1) 65234287Sdim return false; 66234287Sdim 67234287Sdim const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 68234287Sdim Selector Sel = Msg->getSelector(); 69234287Sdim 70234287Sdim if ((isa<ObjCStringLiteral>(Arg) && 71234287Sdim NS.getNSClassId(NSAPI::ClassId_NSString) == II && 72245431Sdim (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || 73245431Sdim NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || 74234287Sdim 75234287Sdim (isa<ObjCArrayLiteral>(Arg) && 76234287Sdim NS.getNSClassId(NSAPI::ClassId_NSArray) == II && 77245431Sdim (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || 78245431Sdim NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || 79234287Sdim 80234287Sdim (isa<ObjCDictionaryLiteral>(Arg) && 81234287Sdim NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && 82245431Sdim (NS.getNSDictionarySelector( 83245431Sdim NSAPI::NSDict_dictionaryWithDictionary) == Sel || 84245431Sdim NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { 85234287Sdim 86234287Sdim commit.replaceWithInner(Msg->getSourceRange(), 87234287Sdim Msg->getArg(0)->getSourceRange()); 88234287Sdim return true; 89234287Sdim } 90234287Sdim 91234287Sdim return false; 92234287Sdim} 93234287Sdim 94234287Sdim//===----------------------------------------------------------------------===// 95234287Sdim// rewriteToObjCSubscriptSyntax. 96234287Sdim//===----------------------------------------------------------------------===// 97234287Sdim 98245431Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors 99245431Sdim/// that the migrator handles) but return their instances as 'id', resulting 100245431Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. 101245431Sdim/// 102245431Sdim/// When checking if we can convert to subscripting syntax, check whether 103245431Sdim/// the receiver is a result of a class method from a hardcoded list of 104245431Sdim/// such classes. In such a case return the specific class as the interface 105245431Sdim/// of the receiver. 106245431Sdim/// 107245431Sdim/// FIXME: Remove this when these classes start using 'instancetype'. 108245431Sdimstatic const ObjCInterfaceDecl * 109245431SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, 110245431Sdim const Expr *Receiver, 111245431Sdim ASTContext &Ctx) { 112245431Sdim assert(IFace && Receiver); 113245431Sdim 114245431Sdim // If the receiver has type 'id'... 115245431Sdim if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) 116245431Sdim return IFace; 117245431Sdim 118245431Sdim const ObjCMessageExpr * 119245431Sdim InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); 120245431Sdim if (!InnerMsg) 121245431Sdim return IFace; 122245431Sdim 123245431Sdim QualType ClassRec; 124245431Sdim switch (InnerMsg->getReceiverKind()) { 125245431Sdim case ObjCMessageExpr::Instance: 126245431Sdim case ObjCMessageExpr::SuperInstance: 127245431Sdim return IFace; 128245431Sdim 129245431Sdim case ObjCMessageExpr::Class: 130245431Sdim ClassRec = InnerMsg->getClassReceiver(); 131245431Sdim break; 132245431Sdim case ObjCMessageExpr::SuperClass: 133245431Sdim ClassRec = InnerMsg->getSuperType(); 134245431Sdim break; 135245431Sdim } 136245431Sdim 137245431Sdim if (ClassRec.isNull()) 138245431Sdim return IFace; 139245431Sdim 140245431Sdim // ...and it is the result of a class message... 141245431Sdim 142245431Sdim const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); 143245431Sdim if (!ObjTy) 144245431Sdim return IFace; 145245431Sdim const ObjCInterfaceDecl *OID = ObjTy->getInterface(); 146245431Sdim 147245431Sdim // ...and the receiving class is NSMapTable or NSLocale, return that 148245431Sdim // class as the receiving interface. 149245431Sdim if (OID->getName() == "NSMapTable" || 150245431Sdim OID->getName() == "NSLocale") 151245431Sdim return OID; 152245431Sdim 153245431Sdim return IFace; 154245431Sdim} 155245431Sdim 156245431Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, 157245431Sdim const ObjCMessageExpr *Msg, 158245431Sdim ASTContext &Ctx, 159245431Sdim Selector subscriptSel) { 160245431Sdim const Expr *Rec = Msg->getInstanceReceiver(); 161245431Sdim if (!Rec) 162245431Sdim return false; 163245431Sdim IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); 164245431Sdim 165245431Sdim if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { 166245431Sdim if (!MD->isUnavailable()) 167245431Sdim return true; 168245431Sdim } 169245431Sdim return false; 170245431Sdim} 171245431Sdim 172245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr); 173245431Sdim 174234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 175245431Sdim if (subscriptOperatorNeedsParens(Receiver)) { 176234287Sdim SourceRange RecRange = Receiver->getSourceRange(); 177234287Sdim commit.insertWrap("(", RecRange, ")"); 178234287Sdim } 179234287Sdim} 180234287Sdim 181245431Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 182245431Sdim Commit &commit) { 183234287Sdim if (Msg->getNumArgs() != 1) 184234287Sdim return false; 185234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 186234287Sdim if (!Rec) 187234287Sdim return false; 188234287Sdim 189234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 190234287Sdim SourceRange RecRange = Rec->getSourceRange(); 191234287Sdim SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 192234287Sdim 193234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 194234287Sdim ArgRange.getBegin()), 195234287Sdim CharSourceRange::getTokenRange(RecRange)); 196234287Sdim commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 197234287Sdim ArgRange); 198234287Sdim commit.insertWrap("[", ArgRange, "]"); 199234287Sdim maybePutParensOnReceiver(Rec, commit); 200234287Sdim return true; 201234287Sdim} 202234287Sdim 203245431Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 204245431Sdim const ObjCMessageExpr *Msg, 205245431Sdim const NSAPI &NS, 206234287Sdim Commit &commit) { 207245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 208245431Sdim NS.getObjectAtIndexedSubscriptSelector())) 209245431Sdim return false; 210245431Sdim return rewriteToSubscriptGetCommon(Msg, commit); 211245431Sdim} 212245431Sdim 213245431Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 214245431Sdim const ObjCMessageExpr *Msg, 215245431Sdim const NSAPI &NS, 216245431Sdim Commit &commit) { 217245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 218245431Sdim NS.getObjectForKeyedSubscriptSelector())) 219245431Sdim return false; 220245431Sdim return rewriteToSubscriptGetCommon(Msg, commit); 221245431Sdim} 222245431Sdim 223245431Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 224245431Sdim const ObjCMessageExpr *Msg, 225245431Sdim const NSAPI &NS, 226245431Sdim Commit &commit) { 227245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 228245431Sdim NS.getSetObjectAtIndexedSubscriptSelector())) 229245431Sdim return false; 230245431Sdim 231234287Sdim if (Msg->getNumArgs() != 2) 232234287Sdim return false; 233234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 234234287Sdim if (!Rec) 235234287Sdim return false; 236234287Sdim 237234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 238234287Sdim SourceRange RecRange = Rec->getSourceRange(); 239234287Sdim SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 240234287Sdim SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 241234287Sdim 242234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 243234287Sdim Arg0Range.getBegin()), 244234287Sdim CharSourceRange::getTokenRange(RecRange)); 245234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 246234287Sdim Arg1Range.getBegin()), 247234287Sdim CharSourceRange::getTokenRange(Arg0Range)); 248234287Sdim commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 249234287Sdim Arg1Range); 250234287Sdim commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 251234287Sdim Arg1Range.getBegin()), 252234287Sdim "] = "); 253234287Sdim maybePutParensOnReceiver(Rec, commit); 254234287Sdim return true; 255234287Sdim} 256234287Sdim 257245431Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 258245431Sdim const ObjCMessageExpr *Msg, 259245431Sdim const NSAPI &NS, 260234287Sdim Commit &commit) { 261245431Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 262245431Sdim NS.getSetObjectForKeyedSubscriptSelector())) 263245431Sdim return false; 264245431Sdim 265234287Sdim if (Msg->getNumArgs() != 2) 266234287Sdim return false; 267234287Sdim const Expr *Rec = Msg->getInstanceReceiver(); 268234287Sdim if (!Rec) 269234287Sdim return false; 270234287Sdim 271234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 272234287Sdim SourceRange RecRange = Rec->getSourceRange(); 273234287Sdim SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 274234287Sdim SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 275234287Sdim 276234287Sdim SourceLocation LocBeforeVal = Arg0Range.getBegin(); 277234287Sdim commit.insertBefore(LocBeforeVal, "] = "); 278234287Sdim commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 279234287Sdim /*beforePreviousInsertions=*/true); 280234287Sdim commit.insertBefore(LocBeforeVal, "["); 281234287Sdim commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 282234287Sdim Arg0Range.getBegin()), 283234287Sdim CharSourceRange::getTokenRange(RecRange)); 284234287Sdim commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 285234287Sdim Arg0Range); 286234287Sdim maybePutParensOnReceiver(Rec, commit); 287234287Sdim return true; 288234287Sdim} 289234287Sdim 290234287Sdimbool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 291245431Sdim const NSAPI &NS, Commit &commit) { 292234287Sdim if (!Msg || Msg->isImplicit() || 293234287Sdim Msg->getReceiverKind() != ObjCMessageExpr::Instance) 294234287Sdim return false; 295234287Sdim const ObjCMethodDecl *Method = Msg->getMethodDecl(); 296234287Sdim if (!Method) 297234287Sdim return false; 298234287Sdim 299252723Sdim const ObjCInterfaceDecl *IFace = 300252723Sdim NS.getASTContext().getObjContainingInterface(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, 328252723Sdim const NSAPI &NS, Commit &commit, 329252723Sdim const ParentMap *PMap); 330234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 331234287Sdim const NSAPI &NS, Commit &commit); 332234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 333234287Sdim const NSAPI &NS, Commit &commit); 334245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 335245431Sdim const NSAPI &NS, Commit &commit); 336245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 337245431Sdim const NSAPI &NS, Commit &commit); 338234287Sdim 339234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 340252723Sdim const NSAPI &NS, Commit &commit, 341252723Sdim const ParentMap *PMap) { 342234287Sdim IdentifierInfo *II = 0; 343245431Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 344234287Sdim return false; 345234287Sdim 346234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 347252723Sdim return rewriteToArrayLiteral(Msg, NS, commit, PMap); 348234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 349234287Sdim return rewriteToDictionaryLiteral(Msg, NS, commit); 350234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 351234287Sdim return rewriteToNumberLiteral(Msg, NS, commit); 352245431Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 353245431Sdim return rewriteToStringBoxedExpression(Msg, NS, commit); 354234287Sdim 355234287Sdim return false; 356234287Sdim} 357234287Sdim 358252723Sdim/// \brief Returns true if the immediate message arguments of \c Msg should not 359252723Sdim/// be rewritten because it will interfere with the rewrite of the parent 360252723Sdim/// message expression. e.g. 361252723Sdim/// \code 362252723Sdim/// [NSDictionary dictionaryWithObjects: 363252723Sdim/// [NSArray arrayWithObjects:@"1", @"2", nil] 364252723Sdim/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; 365252723Sdim/// \endcode 366252723Sdim/// It will return true for this because we are going to rewrite this directly 367252723Sdim/// to a dictionary literal without any array literals. 368252723Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 369252723Sdim const NSAPI &NS); 370252723Sdim 371234287Sdim//===----------------------------------------------------------------------===// 372234287Sdim// rewriteToArrayLiteral. 373234287Sdim//===----------------------------------------------------------------------===// 374234287Sdim 375245431Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object. 376245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit); 377245431Sdim 378234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 379252723Sdim const NSAPI &NS, Commit &commit, 380252723Sdim const ParentMap *PMap) { 381252723Sdim if (PMap) { 382252723Sdim const ObjCMessageExpr *ParentMsg = 383252723Sdim dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); 384252723Sdim if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) 385252723Sdim return false; 386252723Sdim } 387252723Sdim 388234287Sdim Selector Sel = Msg->getSelector(); 389234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 390234287Sdim 391234287Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 392234287Sdim if (Msg->getNumArgs() != 0) 393234287Sdim return false; 394234287Sdim commit.replace(MsgRange, "@[]"); 395234287Sdim return true; 396234287Sdim } 397234287Sdim 398234287Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 399234287Sdim if (Msg->getNumArgs() != 1) 400234287Sdim return false; 401245431Sdim objectifyExpr(Msg->getArg(0), commit); 402234287Sdim SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 403234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 404234287Sdim commit.insertWrap("@[", ArgRange, "]"); 405234287Sdim return true; 406234287Sdim } 407234287Sdim 408245431Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 409245431Sdim Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 410234287Sdim if (Msg->getNumArgs() == 0) 411234287Sdim return false; 412234287Sdim const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 413234287Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 414234287Sdim return false; 415234287Sdim 416245431Sdim for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 417245431Sdim objectifyExpr(Msg->getArg(i), commit); 418245431Sdim 419234287Sdim if (Msg->getNumArgs() == 1) { 420234287Sdim commit.replace(MsgRange, "@[]"); 421234287Sdim return true; 422234287Sdim } 423234287Sdim SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 424234287Sdim Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 425234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 426234287Sdim commit.insertWrap("@[", ArgRange, "]"); 427234287Sdim return true; 428234287Sdim } 429234287Sdim 430234287Sdim return false; 431234287Sdim} 432234287Sdim 433234287Sdim//===----------------------------------------------------------------------===// 434234287Sdim// rewriteToDictionaryLiteral. 435234287Sdim//===----------------------------------------------------------------------===// 436234287Sdim 437252723Sdim/// \brief If \c Msg is an NSArray creation message or literal, this gets the 438252723Sdim/// objects that were used to create it. 439252723Sdim/// \returns true if it is an NSArray and we got objects, or false otherwise. 440252723Sdimstatic bool getNSArrayObjects(const Expr *E, const NSAPI &NS, 441252723Sdim SmallVectorImpl<const Expr *> &Objs) { 442252723Sdim if (!E) 443252723Sdim return false; 444252723Sdim 445252723Sdim E = E->IgnoreParenCasts(); 446252723Sdim if (!E) 447252723Sdim return false; 448252723Sdim 449252723Sdim if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { 450252723Sdim IdentifierInfo *Cls = 0; 451252723Sdim if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) 452252723Sdim return false; 453252723Sdim 454252723Sdim if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) 455252723Sdim return false; 456252723Sdim 457252723Sdim Selector Sel = Msg->getSelector(); 458252723Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) 459252723Sdim return true; // empty array. 460252723Sdim 461252723Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 462252723Sdim if (Msg->getNumArgs() != 1) 463252723Sdim return false; 464252723Sdim Objs.push_back(Msg->getArg(0)); 465252723Sdim return true; 466252723Sdim } 467252723Sdim 468252723Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 469252723Sdim Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 470252723Sdim if (Msg->getNumArgs() == 0) 471252723Sdim return false; 472252723Sdim const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 473252723Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 474252723Sdim return false; 475252723Sdim 476252723Sdim for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 477252723Sdim Objs.push_back(Msg->getArg(i)); 478252723Sdim return true; 479252723Sdim } 480252723Sdim 481252723Sdim } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { 482252723Sdim for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) 483252723Sdim Objs.push_back(ArrLit->getElement(i)); 484252723Sdim return true; 485252723Sdim } 486252723Sdim 487252723Sdim return false; 488252723Sdim} 489252723Sdim 490234287Sdimstatic bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 491234287Sdim const NSAPI &NS, Commit &commit) { 492234287Sdim Selector Sel = Msg->getSelector(); 493234287Sdim SourceRange MsgRange = Msg->getSourceRange(); 494234287Sdim 495234287Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 496234287Sdim if (Msg->getNumArgs() != 0) 497234287Sdim return false; 498234287Sdim commit.replace(MsgRange, "@{}"); 499234287Sdim return true; 500234287Sdim } 501234287Sdim 502234287Sdim if (Sel == NS.getNSDictionarySelector( 503234287Sdim NSAPI::NSDict_dictionaryWithObjectForKey)) { 504234287Sdim if (Msg->getNumArgs() != 2) 505234287Sdim return false; 506245431Sdim 507245431Sdim objectifyExpr(Msg->getArg(0), commit); 508245431Sdim objectifyExpr(Msg->getArg(1), commit); 509245431Sdim 510234287Sdim SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 511234287Sdim SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 512234287Sdim // Insert key before the value. 513234287Sdim commit.insertBefore(ValRange.getBegin(), ": "); 514234287Sdim commit.insertFromRange(ValRange.getBegin(), 515234287Sdim CharSourceRange::getTokenRange(KeyRange), 516234287Sdim /*afterToken=*/false, /*beforePreviousInsertions=*/true); 517234287Sdim commit.insertBefore(ValRange.getBegin(), "@{"); 518234287Sdim commit.insertAfterToken(ValRange.getEnd(), "}"); 519234287Sdim commit.replaceWithInner(MsgRange, ValRange); 520234287Sdim return true; 521234287Sdim } 522234287Sdim 523234287Sdim if (Sel == NS.getNSDictionarySelector( 524245431Sdim NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 525245431Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 526234287Sdim if (Msg->getNumArgs() % 2 != 1) 527234287Sdim return false; 528234287Sdim unsigned SentinelIdx = Msg->getNumArgs() - 1; 529234287Sdim const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 530234287Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 531234287Sdim return false; 532234287Sdim 533234287Sdim if (Msg->getNumArgs() == 1) { 534234287Sdim commit.replace(MsgRange, "@{}"); 535234287Sdim return true; 536234287Sdim } 537234287Sdim 538234287Sdim for (unsigned i = 0; i < SentinelIdx; i += 2) { 539245431Sdim objectifyExpr(Msg->getArg(i), commit); 540245431Sdim objectifyExpr(Msg->getArg(i+1), commit); 541245431Sdim 542234287Sdim SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 543234287Sdim SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 544234287Sdim // Insert value after key. 545234287Sdim commit.insertAfterToken(KeyRange.getEnd(), ": "); 546234287Sdim commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 547234287Sdim commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 548234287Sdim KeyRange.getBegin())); 549234287Sdim } 550234287Sdim // Range of arguments up until and including the last key. 551234287Sdim // The sentinel and first value are cut off, the value will move after the 552234287Sdim // key. 553234287Sdim SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 554234287Sdim Msg->getArg(SentinelIdx-1)->getLocEnd()); 555234287Sdim commit.insertWrap("@{", ArgRange, "}"); 556234287Sdim commit.replaceWithInner(MsgRange, ArgRange); 557234287Sdim return true; 558234287Sdim } 559234287Sdim 560252723Sdim if (Sel == NS.getNSDictionarySelector( 561252723Sdim NSAPI::NSDict_dictionaryWithObjectsForKeys) || 562252723Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 563252723Sdim if (Msg->getNumArgs() != 2) 564252723Sdim return false; 565252723Sdim 566252723Sdim SmallVector<const Expr *, 8> Vals; 567252723Sdim if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 568252723Sdim return false; 569252723Sdim 570252723Sdim SmallVector<const Expr *, 8> Keys; 571252723Sdim if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 572252723Sdim return false; 573252723Sdim 574252723Sdim if (Vals.size() != Keys.size()) 575252723Sdim return false; 576252723Sdim 577252723Sdim if (Vals.empty()) { 578252723Sdim commit.replace(MsgRange, "@{}"); 579252723Sdim return true; 580252723Sdim } 581252723Sdim 582252723Sdim for (unsigned i = 0, n = Vals.size(); i < n; ++i) { 583252723Sdim objectifyExpr(Vals[i], commit); 584252723Sdim objectifyExpr(Keys[i], commit); 585252723Sdim 586252723Sdim SourceRange ValRange = Vals[i]->getSourceRange(); 587252723Sdim SourceRange KeyRange = Keys[i]->getSourceRange(); 588252723Sdim // Insert value after key. 589252723Sdim commit.insertAfterToken(KeyRange.getEnd(), ": "); 590252723Sdim commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 591252723Sdim } 592252723Sdim // Range of arguments up until and including the last key. 593252723Sdim // The first value is cut off, the value will move after the key. 594252723Sdim SourceRange ArgRange(Keys.front()->getLocStart(), 595252723Sdim Keys.back()->getLocEnd()); 596252723Sdim commit.insertWrap("@{", ArgRange, "}"); 597252723Sdim commit.replaceWithInner(MsgRange, ArgRange); 598252723Sdim return true; 599252723Sdim } 600252723Sdim 601234287Sdim return false; 602234287Sdim} 603234287Sdim 604252723Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 605252723Sdim const NSAPI &NS) { 606252723Sdim if (!Msg) 607252723Sdim return false; 608252723Sdim 609252723Sdim IdentifierInfo *II = 0; 610252723Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 611252723Sdim return false; 612252723Sdim 613252723Sdim if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 614252723Sdim return false; 615252723Sdim 616252723Sdim Selector Sel = Msg->getSelector(); 617252723Sdim if (Sel == NS.getNSDictionarySelector( 618252723Sdim NSAPI::NSDict_dictionaryWithObjectsForKeys) || 619252723Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 620252723Sdim if (Msg->getNumArgs() != 2) 621252723Sdim return false; 622252723Sdim 623252723Sdim SmallVector<const Expr *, 8> Vals; 624252723Sdim if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 625252723Sdim return false; 626252723Sdim 627252723Sdim SmallVector<const Expr *, 8> Keys; 628252723Sdim if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 629252723Sdim return false; 630252723Sdim 631252723Sdim if (Vals.size() != Keys.size()) 632252723Sdim return false; 633252723Sdim 634252723Sdim return true; 635252723Sdim } 636252723Sdim 637252723Sdim return false; 638252723Sdim} 639252723Sdim 640234287Sdim//===----------------------------------------------------------------------===// 641234287Sdim// rewriteToNumberLiteral. 642234287Sdim//===----------------------------------------------------------------------===// 643234287Sdim 644234287Sdimstatic bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 645234287Sdim const CharacterLiteral *Arg, 646234287Sdim const NSAPI &NS, Commit &commit) { 647234287Sdim if (Arg->getKind() != CharacterLiteral::Ascii) 648234287Sdim return false; 649234287Sdim if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 650234287Sdim Msg->getSelector())) { 651234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 652234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 653234287Sdim commit.insert(ArgRange.getBegin(), "@"); 654234287Sdim return true; 655234287Sdim } 656234287Sdim 657245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 658234287Sdim} 659234287Sdim 660234287Sdimstatic bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 661234287Sdim const Expr *Arg, 662234287Sdim const NSAPI &NS, Commit &commit) { 663234287Sdim if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 664234287Sdim Msg->getSelector())) { 665234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 666234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 667234287Sdim commit.insert(ArgRange.getBegin(), "@"); 668234287Sdim return true; 669234287Sdim } 670234287Sdim 671245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 672234287Sdim} 673234287Sdim 674234287Sdimnamespace { 675234287Sdim 676234287Sdimstruct LiteralInfo { 677234287Sdim bool Hex, Octal; 678234287Sdim StringRef U, F, L, LL; 679234287Sdim CharSourceRange WithoutSuffRange; 680234287Sdim}; 681234287Sdim 682234287Sdim} 683234287Sdim 684234287Sdimstatic bool getLiteralInfo(SourceRange literalRange, 685234287Sdim bool isFloat, bool isIntZero, 686234287Sdim ASTContext &Ctx, LiteralInfo &Info) { 687234287Sdim if (literalRange.getBegin().isMacroID() || 688234287Sdim literalRange.getEnd().isMacroID()) 689234287Sdim return false; 690234287Sdim StringRef text = Lexer::getSourceText( 691234287Sdim CharSourceRange::getTokenRange(literalRange), 692234287Sdim Ctx.getSourceManager(), Ctx.getLangOpts()); 693234287Sdim if (text.empty()) 694234287Sdim return false; 695234287Sdim 696252723Sdim Optional<bool> UpperU, UpperL; 697234287Sdim bool UpperF = false; 698234287Sdim 699234287Sdim struct Suff { 700234287Sdim static bool has(StringRef suff, StringRef &text) { 701234287Sdim if (text.endswith(suff)) { 702234287Sdim text = text.substr(0, text.size()-suff.size()); 703234287Sdim return true; 704234287Sdim } 705234287Sdim return false; 706234287Sdim } 707234287Sdim }; 708234287Sdim 709234287Sdim while (1) { 710234287Sdim if (Suff::has("u", text)) { 711234287Sdim UpperU = false; 712234287Sdim } else if (Suff::has("U", text)) { 713234287Sdim UpperU = true; 714234287Sdim } else if (Suff::has("ll", text)) { 715234287Sdim UpperL = false; 716234287Sdim } else if (Suff::has("LL", text)) { 717234287Sdim UpperL = true; 718234287Sdim } else if (Suff::has("l", text)) { 719234287Sdim UpperL = false; 720234287Sdim } else if (Suff::has("L", text)) { 721234287Sdim UpperL = true; 722234287Sdim } else if (isFloat && Suff::has("f", text)) { 723234287Sdim UpperF = false; 724234287Sdim } else if (isFloat && Suff::has("F", text)) { 725234287Sdim UpperF = true; 726234287Sdim } else 727234287Sdim break; 728234287Sdim } 729234287Sdim 730234287Sdim if (!UpperU.hasValue() && !UpperL.hasValue()) 731234287Sdim UpperU = UpperL = true; 732234287Sdim else if (UpperU.hasValue() && !UpperL.hasValue()) 733234287Sdim UpperL = UpperU; 734234287Sdim else if (UpperL.hasValue() && !UpperU.hasValue()) 735234287Sdim UpperU = UpperL; 736234287Sdim 737234287Sdim Info.U = *UpperU ? "U" : "u"; 738234287Sdim Info.L = *UpperL ? "L" : "l"; 739234287Sdim Info.LL = *UpperL ? "LL" : "ll"; 740234287Sdim Info.F = UpperF ? "F" : "f"; 741234287Sdim 742234287Sdim Info.Hex = Info.Octal = false; 743234287Sdim if (text.startswith("0x")) 744234287Sdim Info.Hex = true; 745234287Sdim else if (!isFloat && !isIntZero && text.startswith("0")) 746234287Sdim Info.Octal = true; 747234287Sdim 748234287Sdim SourceLocation B = literalRange.getBegin(); 749234287Sdim Info.WithoutSuffRange = 750234287Sdim CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 751234287Sdim return true; 752234287Sdim} 753234287Sdim 754234287Sdimstatic bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 755234287Sdim const NSAPI &NS, Commit &commit) { 756234287Sdim if (Msg->getNumArgs() != 1) 757234287Sdim return false; 758234287Sdim 759234287Sdim const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 760234287Sdim if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 761234287Sdim return rewriteToCharLiteral(Msg, CharE, NS, commit); 762234287Sdim if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 763234287Sdim return rewriteToBoolLiteral(Msg, BE, NS, commit); 764234287Sdim if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 765234287Sdim return rewriteToBoolLiteral(Msg, BE, NS, commit); 766234287Sdim 767234287Sdim const Expr *literalE = Arg; 768234287Sdim if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 769234287Sdim if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 770234287Sdim literalE = UOE->getSubExpr(); 771234287Sdim } 772234287Sdim 773245431Sdim // Only integer and floating literals, otherwise try to rewrite to boxed 774245431Sdim // expression. 775234287Sdim if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 776245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 777234287Sdim 778234287Sdim ASTContext &Ctx = NS.getASTContext(); 779234287Sdim Selector Sel = Msg->getSelector(); 780252723Sdim Optional<NSAPI::NSNumberLiteralMethodKind> 781234287Sdim MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 782234287Sdim if (!MKOpt) 783234287Sdim return false; 784234287Sdim NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 785234287Sdim 786234287Sdim bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 787234287Sdim bool CallIsFloating = false, CallIsDouble = false; 788234287Sdim 789234287Sdim switch (MK) { 790234287Sdim // We cannot have these calls with int/float literals. 791234287Sdim case NSAPI::NSNumberWithChar: 792234287Sdim case NSAPI::NSNumberWithUnsignedChar: 793234287Sdim case NSAPI::NSNumberWithShort: 794234287Sdim case NSAPI::NSNumberWithUnsignedShort: 795234287Sdim case NSAPI::NSNumberWithBool: 796245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 797234287Sdim 798234287Sdim case NSAPI::NSNumberWithUnsignedInt: 799234287Sdim case NSAPI::NSNumberWithUnsignedInteger: 800234287Sdim CallIsUnsigned = true; 801234287Sdim case NSAPI::NSNumberWithInt: 802234287Sdim case NSAPI::NSNumberWithInteger: 803234287Sdim break; 804234287Sdim 805234287Sdim case NSAPI::NSNumberWithUnsignedLong: 806234287Sdim CallIsUnsigned = true; 807234287Sdim case NSAPI::NSNumberWithLong: 808234287Sdim CallIsLong = true; 809234287Sdim break; 810234287Sdim 811234287Sdim case NSAPI::NSNumberWithUnsignedLongLong: 812234287Sdim CallIsUnsigned = true; 813234287Sdim case NSAPI::NSNumberWithLongLong: 814234287Sdim CallIsLongLong = true; 815234287Sdim break; 816234287Sdim 817234287Sdim case NSAPI::NSNumberWithDouble: 818234287Sdim CallIsDouble = true; 819234287Sdim case NSAPI::NSNumberWithFloat: 820234287Sdim CallIsFloating = true; 821234287Sdim break; 822234287Sdim } 823234287Sdim 824234287Sdim SourceRange ArgRange = Arg->getSourceRange(); 825234287Sdim QualType ArgTy = Arg->getType(); 826234287Sdim QualType CallTy = Msg->getArg(0)->getType(); 827234287Sdim 828234287Sdim // Check for the easy case, the literal maps directly to the call. 829234287Sdim if (Ctx.hasSameType(ArgTy, CallTy)) { 830234287Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 831234287Sdim commit.insert(ArgRange.getBegin(), "@"); 832234287Sdim return true; 833234287Sdim } 834234287Sdim 835234287Sdim // We will need to modify the literal suffix to get the same type as the call. 836245431Sdim // Try with boxed expression if it came from a macro. 837234287Sdim if (ArgRange.getBegin().isMacroID()) 838245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 839234287Sdim 840234287Sdim bool LitIsFloat = ArgTy->isFloatingType(); 841245431Sdim // For a float passed to integer call, don't try rewriting to objc literal. 842245431Sdim // It is difficult and a very uncommon case anyway. 843245431Sdim // But try with boxed expression. 844234287Sdim if (LitIsFloat && !CallIsFloating) 845245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 846234287Sdim 847234287Sdim // Try to modify the literal make it the same type as the method call. 848234287Sdim // -Modify the suffix, and/or 849234287Sdim // -Change integer to float 850234287Sdim 851234287Sdim LiteralInfo LitInfo; 852234287Sdim bool isIntZero = false; 853234287Sdim if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 854234287Sdim isIntZero = !IntE->getValue().getBoolValue(); 855234287Sdim if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 856245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 857234287Sdim 858234287Sdim // Not easy to do int -> float with hex/octal and uncommon anyway. 859234287Sdim if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 860245431Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 861234287Sdim 862234287Sdim SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 863234287Sdim SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 864234287Sdim 865234287Sdim commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 866234287Sdim LitInfo.WithoutSuffRange); 867234287Sdim commit.insert(LitB, "@"); 868234287Sdim 869234287Sdim if (!LitIsFloat && CallIsFloating) 870234287Sdim commit.insert(LitE, ".0"); 871234287Sdim 872234287Sdim if (CallIsFloating) { 873234287Sdim if (!CallIsDouble) 874234287Sdim commit.insert(LitE, LitInfo.F); 875234287Sdim } else { 876234287Sdim if (CallIsUnsigned) 877234287Sdim commit.insert(LitE, LitInfo.U); 878234287Sdim 879234287Sdim if (CallIsLong) 880234287Sdim commit.insert(LitE, LitInfo.L); 881234287Sdim else if (CallIsLongLong) 882234287Sdim commit.insert(LitE, LitInfo.LL); 883234287Sdim } 884234287Sdim return true; 885234287Sdim} 886245431Sdim 887245431Sdim// FIXME: Make determination of operator precedence more general and 888245431Sdim// make it broadly available. 889245431Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 890245431Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 891245431Sdim if (isa<ArraySubscriptExpr>(Expr) || 892245431Sdim isa<CallExpr>(Expr) || 893245431Sdim isa<DeclRefExpr>(Expr) || 894245431Sdim isa<CXXNamedCastExpr>(Expr) || 895245431Sdim isa<CXXConstructExpr>(Expr) || 896245431Sdim isa<CXXThisExpr>(Expr) || 897245431Sdim isa<CXXTypeidExpr>(Expr) || 898245431Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 899245431Sdim isa<ObjCMessageExpr>(Expr) || 900245431Sdim isa<ObjCPropertyRefExpr>(Expr) || 901245431Sdim isa<ObjCProtocolExpr>(Expr) || 902245431Sdim isa<MemberExpr>(Expr) || 903245431Sdim isa<ObjCIvarRefExpr>(Expr) || 904245431Sdim isa<ParenExpr>(FullExpr) || 905245431Sdim isa<ParenListExpr>(Expr) || 906245431Sdim isa<SizeOfPackExpr>(Expr)) 907245431Sdim return false; 908245431Sdim 909245431Sdim return true; 910245431Sdim} 911245431Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) { 912245431Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 913245431Sdim if (isa<ArraySubscriptExpr>(Expr) || 914245431Sdim isa<CallExpr>(Expr) || 915245431Sdim isa<DeclRefExpr>(Expr) || 916245431Sdim isa<CastExpr>(Expr) || 917245431Sdim isa<CXXNewExpr>(Expr) || 918245431Sdim isa<CXXConstructExpr>(Expr) || 919245431Sdim isa<CXXDeleteExpr>(Expr) || 920245431Sdim isa<CXXNoexceptExpr>(Expr) || 921245431Sdim isa<CXXPseudoDestructorExpr>(Expr) || 922245431Sdim isa<CXXScalarValueInitExpr>(Expr) || 923245431Sdim isa<CXXThisExpr>(Expr) || 924245431Sdim isa<CXXTypeidExpr>(Expr) || 925245431Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 926245431Sdim isa<ObjCMessageExpr>(Expr) || 927245431Sdim isa<ObjCPropertyRefExpr>(Expr) || 928245431Sdim isa<ObjCProtocolExpr>(Expr) || 929245431Sdim isa<MemberExpr>(Expr) || 930245431Sdim isa<ObjCIvarRefExpr>(Expr) || 931245431Sdim isa<ParenExpr>(FullExpr) || 932245431Sdim isa<ParenListExpr>(Expr) || 933245431Sdim isa<SizeOfPackExpr>(Expr) || 934245431Sdim isa<UnaryOperator>(Expr)) 935245431Sdim return false; 936245431Sdim 937245431Sdim return true; 938245431Sdim} 939245431Sdim 940245431Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) { 941245431Sdim if (!E) return; 942245431Sdim 943245431Sdim QualType T = E->getType(); 944245431Sdim if (T->isObjCObjectPointerType()) { 945245431Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 946245431Sdim if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 947245431Sdim return; 948245431Sdim } else { 949245431Sdim return; 950245431Sdim } 951245431Sdim } else if (!T->isPointerType()) { 952245431Sdim return; 953245431Sdim } 954245431Sdim 955245431Sdim SourceRange Range = E->getSourceRange(); 956245431Sdim if (castOperatorNeedsParens(E)) 957245431Sdim commit.insertWrap("(", Range, ")"); 958245431Sdim commit.insertBefore(Range.getBegin(), "(id)"); 959245431Sdim} 960245431Sdim 961245431Sdim//===----------------------------------------------------------------------===// 962245431Sdim// rewriteToNumericBoxedExpression. 963245431Sdim//===----------------------------------------------------------------------===// 964245431Sdim 965245431Sdimstatic bool isEnumConstant(const Expr *E) { 966245431Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 967245431Sdim if (const ValueDecl *VD = DRE->getDecl()) 968245431Sdim return isa<EnumConstantDecl>(VD); 969245431Sdim 970245431Sdim return false; 971245431Sdim} 972245431Sdim 973245431Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 974245431Sdim const NSAPI &NS, Commit &commit) { 975245431Sdim if (Msg->getNumArgs() != 1) 976245431Sdim return false; 977245431Sdim 978245431Sdim const Expr *Arg = Msg->getArg(0); 979245431Sdim if (Arg->isTypeDependent()) 980245431Sdim return false; 981245431Sdim 982245431Sdim ASTContext &Ctx = NS.getASTContext(); 983245431Sdim Selector Sel = Msg->getSelector(); 984252723Sdim Optional<NSAPI::NSNumberLiteralMethodKind> 985245431Sdim MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 986245431Sdim if (!MKOpt) 987245431Sdim return false; 988245431Sdim NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 989245431Sdim 990245431Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 991245431Sdim QualType FinalTy = Arg->getType(); 992245431Sdim QualType OrigTy = OrigArg->getType(); 993245431Sdim uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 994245431Sdim uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 995245431Sdim 996245431Sdim bool isTruncated = FinalTySize < OrigTySize; 997245431Sdim bool needsCast = false; 998245431Sdim 999245431Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 1000245431Sdim switch (ICE->getCastKind()) { 1001245431Sdim case CK_LValueToRValue: 1002245431Sdim case CK_NoOp: 1003245431Sdim case CK_UserDefinedConversion: 1004245431Sdim break; 1005245431Sdim 1006245431Sdim case CK_IntegralCast: { 1007245431Sdim if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 1008245431Sdim break; 1009245431Sdim // Be more liberal with Integer/UnsignedInteger which are very commonly 1010245431Sdim // used. 1011245431Sdim if ((MK == NSAPI::NSNumberWithInteger || 1012245431Sdim MK == NSAPI::NSNumberWithUnsignedInteger) && 1013245431Sdim !isTruncated) { 1014245431Sdim if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 1015245431Sdim break; 1016245431Sdim if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 1017245431Sdim OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 1018245431Sdim break; 1019245431Sdim } 1020245431Sdim 1021245431Sdim needsCast = true; 1022245431Sdim break; 1023245431Sdim } 1024245431Sdim 1025245431Sdim case CK_PointerToBoolean: 1026245431Sdim case CK_IntegralToBoolean: 1027245431Sdim case CK_IntegralToFloating: 1028245431Sdim case CK_FloatingToIntegral: 1029245431Sdim case CK_FloatingToBoolean: 1030245431Sdim case CK_FloatingCast: 1031245431Sdim case CK_FloatingComplexToReal: 1032245431Sdim case CK_FloatingComplexToBoolean: 1033245431Sdim case CK_IntegralComplexToReal: 1034245431Sdim case CK_IntegralComplexToBoolean: 1035245431Sdim case CK_AtomicToNonAtomic: 1036245431Sdim needsCast = true; 1037245431Sdim break; 1038245431Sdim 1039245431Sdim case CK_Dependent: 1040245431Sdim case CK_BitCast: 1041245431Sdim case CK_LValueBitCast: 1042245431Sdim case CK_BaseToDerived: 1043245431Sdim case CK_DerivedToBase: 1044245431Sdim case CK_UncheckedDerivedToBase: 1045245431Sdim case CK_Dynamic: 1046245431Sdim case CK_ToUnion: 1047245431Sdim case CK_ArrayToPointerDecay: 1048245431Sdim case CK_FunctionToPointerDecay: 1049245431Sdim case CK_NullToPointer: 1050245431Sdim case CK_NullToMemberPointer: 1051245431Sdim case CK_BaseToDerivedMemberPointer: 1052245431Sdim case CK_DerivedToBaseMemberPointer: 1053245431Sdim case CK_MemberPointerToBoolean: 1054245431Sdim case CK_ReinterpretMemberPointer: 1055245431Sdim case CK_ConstructorConversion: 1056245431Sdim case CK_IntegralToPointer: 1057245431Sdim case CK_PointerToIntegral: 1058245431Sdim case CK_ToVoid: 1059245431Sdim case CK_VectorSplat: 1060245431Sdim case CK_CPointerToObjCPointerCast: 1061245431Sdim case CK_BlockPointerToObjCPointerCast: 1062245431Sdim case CK_AnyPointerToBlockPointerCast: 1063245431Sdim case CK_ObjCObjectLValueCast: 1064245431Sdim case CK_FloatingRealToComplex: 1065245431Sdim case CK_FloatingComplexCast: 1066245431Sdim case CK_FloatingComplexToIntegralComplex: 1067245431Sdim case CK_IntegralRealToComplex: 1068245431Sdim case CK_IntegralComplexCast: 1069245431Sdim case CK_IntegralComplexToFloatingComplex: 1070245431Sdim case CK_ARCProduceObject: 1071245431Sdim case CK_ARCConsumeObject: 1072245431Sdim case CK_ARCReclaimReturnedObject: 1073245431Sdim case CK_ARCExtendBlockObject: 1074245431Sdim case CK_NonAtomicToAtomic: 1075245431Sdim case CK_CopyAndAutoreleaseBlockObject: 1076245431Sdim case CK_BuiltinFnToFnPtr: 1077252723Sdim case CK_ZeroToOCLEvent: 1078245431Sdim return false; 1079245431Sdim } 1080245431Sdim } 1081245431Sdim 1082245431Sdim if (needsCast) { 1083245431Sdim DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 1084245431Sdim // FIXME: Use a custom category name to distinguish migration diagnostics. 1085245431Sdim unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 1086245431Sdim "converting to boxing syntax requires casting %0 to %1"); 1087245431Sdim Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 1088245431Sdim << Msg->getSourceRange(); 1089245431Sdim return false; 1090245431Sdim } 1091245431Sdim 1092245431Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 1093245431Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1094245431Sdim 1095245431Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1096245431Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 1097245431Sdim else 1098245431Sdim commit.insertWrap("@(", ArgRange, ")"); 1099245431Sdim 1100245431Sdim return true; 1101245431Sdim} 1102245431Sdim 1103245431Sdim//===----------------------------------------------------------------------===// 1104245431Sdim// rewriteToStringBoxedExpression. 1105245431Sdim//===----------------------------------------------------------------------===// 1106245431Sdim 1107245431Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper( 1108245431Sdim const ObjCMessageExpr *Msg, 1109245431Sdim const NSAPI &NS, Commit &commit) { 1110245431Sdim const Expr *Arg = Msg->getArg(0); 1111245431Sdim if (Arg->isTypeDependent()) 1112245431Sdim return false; 1113245431Sdim 1114245431Sdim ASTContext &Ctx = NS.getASTContext(); 1115245431Sdim 1116245431Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 1117245431Sdim QualType OrigTy = OrigArg->getType(); 1118245431Sdim if (OrigTy->isArrayType()) 1119245431Sdim OrigTy = Ctx.getArrayDecayedType(OrigTy); 1120245431Sdim 1121245431Sdim if (const StringLiteral * 1122245431Sdim StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 1123245431Sdim commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 1124245431Sdim commit.insert(StrE->getLocStart(), "@"); 1125245431Sdim return true; 1126245431Sdim } 1127245431Sdim 1128245431Sdim if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 1129245431Sdim QualType PointeeType = PT->getPointeeType(); 1130245431Sdim if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 1131245431Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 1132245431Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1133245431Sdim 1134245431Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1135245431Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 1136245431Sdim else 1137245431Sdim commit.insertWrap("@(", ArgRange, ")"); 1138245431Sdim 1139245431Sdim return true; 1140245431Sdim } 1141245431Sdim } 1142245431Sdim 1143245431Sdim return false; 1144245431Sdim} 1145245431Sdim 1146245431Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 1147245431Sdim const NSAPI &NS, Commit &commit) { 1148245431Sdim Selector Sel = Msg->getSelector(); 1149245431Sdim 1150245431Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 1151245431Sdim Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 1152245431Sdim if (Msg->getNumArgs() != 1) 1153245431Sdim return false; 1154245431Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1155245431Sdim } 1156245431Sdim 1157245431Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1158245431Sdim if (Msg->getNumArgs() != 2) 1159245431Sdim return false; 1160245431Sdim 1161245431Sdim const Expr *encodingArg = Msg->getArg(1); 1162245431Sdim if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1163245431Sdim NS.isNSASCIIStringEncodingConstant(encodingArg)) 1164245431Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1165245431Sdim } 1166245431Sdim 1167245431Sdim return false; 1168245431Sdim} 1169