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" 15239462Sdim#include "clang/AST/ASTContext.h" 16249423Sdim#include "clang/AST/ExprCXX.h" 17234287Sdim#include "clang/AST/ExprObjC.h" 18234287Sdim#include "clang/AST/NSAPI.h" 19249423Sdim#include "clang/AST/ParentMap.h" 20249423Sdim#include "clang/Edit/Commit.h" 21249423Sdim#include "clang/Lex/Lexer.h" 22234287Sdim 23234287Sdimusing namespace clang; 24234287Sdimusing namespace edit; 25234287Sdim 26234287Sdimstatic bool checkForLiteralCreation(const ObjCMessageExpr *Msg, 27239462Sdim IdentifierInfo *&ClassId, 28239462Sdim 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 40239462Sdim // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, 41239462Sdim // since the change from +1 to +0 will be handled fine by ARC. 42239462Sdim if (LangOpts.ObjCAutoRefCount) { 43239462Sdim if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { 44239462Sdim if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( 45239462Sdim Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { 46239462Sdim if (Rec->getMethodFamily() == OMF_alloc) 47239462Sdim return true; 48239462Sdim } 49239462Sdim } 50239462Sdim } 51239462Sdim 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; 62239462Sdim 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 && 72239462Sdim (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || 73239462Sdim NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || 74234287Sdim 75234287Sdim (isa<ObjCArrayLiteral>(Arg) && 76234287Sdim NS.getNSClassId(NSAPI::ClassId_NSArray) == II && 77239462Sdim (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || 78239462Sdim NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || 79234287Sdim 80234287Sdim (isa<ObjCDictionaryLiteral>(Arg) && 81234287Sdim NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && 82239462Sdim (NS.getNSDictionarySelector( 83239462Sdim NSAPI::NSDict_dictionaryWithDictionary) == Sel || 84239462Sdim 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 98239462Sdim/// \brief Check for classes that accept 'objectForKey:' (or the other selectors 99239462Sdim/// that the migrator handles) but return their instances as 'id', resulting 100239462Sdim/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. 101239462Sdim/// 102239462Sdim/// When checking if we can convert to subscripting syntax, check whether 103239462Sdim/// the receiver is a result of a class method from a hardcoded list of 104239462Sdim/// such classes. In such a case return the specific class as the interface 105239462Sdim/// of the receiver. 106239462Sdim/// 107239462Sdim/// FIXME: Remove this when these classes start using 'instancetype'. 108239462Sdimstatic const ObjCInterfaceDecl * 109239462SdimmaybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, 110239462Sdim const Expr *Receiver, 111239462Sdim ASTContext &Ctx) { 112239462Sdim assert(IFace && Receiver); 113239462Sdim 114239462Sdim // If the receiver has type 'id'... 115239462Sdim if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) 116239462Sdim return IFace; 117239462Sdim 118239462Sdim const ObjCMessageExpr * 119239462Sdim InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); 120239462Sdim if (!InnerMsg) 121239462Sdim return IFace; 122239462Sdim 123239462Sdim QualType ClassRec; 124239462Sdim switch (InnerMsg->getReceiverKind()) { 125239462Sdim case ObjCMessageExpr::Instance: 126239462Sdim case ObjCMessageExpr::SuperInstance: 127239462Sdim return IFace; 128239462Sdim 129239462Sdim case ObjCMessageExpr::Class: 130239462Sdim ClassRec = InnerMsg->getClassReceiver(); 131239462Sdim break; 132239462Sdim case ObjCMessageExpr::SuperClass: 133239462Sdim ClassRec = InnerMsg->getSuperType(); 134239462Sdim break; 135239462Sdim } 136239462Sdim 137239462Sdim if (ClassRec.isNull()) 138239462Sdim return IFace; 139239462Sdim 140239462Sdim // ...and it is the result of a class message... 141239462Sdim 142239462Sdim const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); 143239462Sdim if (!ObjTy) 144239462Sdim return IFace; 145239462Sdim const ObjCInterfaceDecl *OID = ObjTy->getInterface(); 146239462Sdim 147239462Sdim // ...and the receiving class is NSMapTable or NSLocale, return that 148239462Sdim // class as the receiving interface. 149239462Sdim if (OID->getName() == "NSMapTable" || 150239462Sdim OID->getName() == "NSLocale") 151239462Sdim return OID; 152239462Sdim 153239462Sdim return IFace; 154239462Sdim} 155239462Sdim 156239462Sdimstatic bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, 157239462Sdim const ObjCMessageExpr *Msg, 158239462Sdim ASTContext &Ctx, 159239462Sdim Selector subscriptSel) { 160239462Sdim const Expr *Rec = Msg->getInstanceReceiver(); 161239462Sdim if (!Rec) 162239462Sdim return false; 163239462Sdim IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); 164239462Sdim 165239462Sdim if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { 166239462Sdim if (!MD->isUnavailable()) 167239462Sdim return true; 168239462Sdim } 169239462Sdim return false; 170239462Sdim} 171239462Sdim 172239462Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr); 173239462Sdim 174234287Sdimstatic void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 175239462Sdim if (subscriptOperatorNeedsParens(Receiver)) { 176234287Sdim SourceRange RecRange = Receiver->getSourceRange(); 177234287Sdim commit.insertWrap("(", RecRange, ")"); 178234287Sdim } 179234287Sdim} 180234287Sdim 181239462Sdimstatic bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 182239462Sdim 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 203239462Sdimstatic bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 204239462Sdim const ObjCMessageExpr *Msg, 205239462Sdim const NSAPI &NS, 206234287Sdim Commit &commit) { 207239462Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 208239462Sdim NS.getObjectAtIndexedSubscriptSelector())) 209239462Sdim return false; 210239462Sdim return rewriteToSubscriptGetCommon(Msg, commit); 211239462Sdim} 212239462Sdim 213239462Sdimstatic bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 214239462Sdim const ObjCMessageExpr *Msg, 215239462Sdim const NSAPI &NS, 216239462Sdim Commit &commit) { 217239462Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 218239462Sdim NS.getObjectForKeyedSubscriptSelector())) 219239462Sdim return false; 220239462Sdim return rewriteToSubscriptGetCommon(Msg, commit); 221239462Sdim} 222239462Sdim 223239462Sdimstatic bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 224239462Sdim const ObjCMessageExpr *Msg, 225239462Sdim const NSAPI &NS, 226239462Sdim Commit &commit) { 227239462Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 228239462Sdim NS.getSetObjectAtIndexedSubscriptSelector())) 229239462Sdim return false; 230239462Sdim 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 257239462Sdimstatic bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 258239462Sdim const ObjCMessageExpr *Msg, 259239462Sdim const NSAPI &NS, 260234287Sdim Commit &commit) { 261239462Sdim if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 262239462Sdim NS.getSetObjectForKeyedSubscriptSelector())) 263239462Sdim return false; 264239462Sdim 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, 291239462Sdim 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 299249423Sdim const ObjCInterfaceDecl *IFace = 300249423Sdim NS.getASTContext().getObjContainingInterface(Method); 301234287Sdim if (!IFace) 302234287Sdim return false; 303234287Sdim Selector Sel = Msg->getSelector(); 304234287Sdim 305239462Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 306239462Sdim return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 307234287Sdim 308239462Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 309239462Sdim return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 310239462Sdim 311234287Sdim if (Msg->getNumArgs() != 2) 312234287Sdim return false; 313234287Sdim 314239462Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 315239462Sdim return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 316234287Sdim 317239462Sdim if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 318239462Sdim return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 319234287Sdim 320234287Sdim return false; 321234287Sdim} 322234287Sdim 323234287Sdim//===----------------------------------------------------------------------===// 324234287Sdim// rewriteToObjCLiteralSyntax. 325234287Sdim//===----------------------------------------------------------------------===// 326234287Sdim 327234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 328249423Sdim const NSAPI &NS, Commit &commit, 329249423Sdim 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); 334239462Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 335239462Sdim const NSAPI &NS, Commit &commit); 336239462Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 337239462Sdim const NSAPI &NS, Commit &commit); 338234287Sdim 339234287Sdimbool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 340249423Sdim const NSAPI &NS, Commit &commit, 341249423Sdim const ParentMap *PMap) { 342234287Sdim IdentifierInfo *II = 0; 343239462Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 344234287Sdim return false; 345234287Sdim 346234287Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 347249423Sdim 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); 352239462Sdim if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 353239462Sdim return rewriteToStringBoxedExpression(Msg, NS, commit); 354234287Sdim 355234287Sdim return false; 356234287Sdim} 357234287Sdim 358249423Sdim/// \brief Returns true if the immediate message arguments of \c Msg should not 359249423Sdim/// be rewritten because it will interfere with the rewrite of the parent 360249423Sdim/// message expression. e.g. 361249423Sdim/// \code 362249423Sdim/// [NSDictionary dictionaryWithObjects: 363249423Sdim/// [NSArray arrayWithObjects:@"1", @"2", nil] 364249423Sdim/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; 365249423Sdim/// \endcode 366249423Sdim/// It will return true for this because we are going to rewrite this directly 367249423Sdim/// to a dictionary literal without any array literals. 368249423Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 369249423Sdim const NSAPI &NS); 370249423Sdim 371234287Sdim//===----------------------------------------------------------------------===// 372234287Sdim// rewriteToArrayLiteral. 373234287Sdim//===----------------------------------------------------------------------===// 374234287Sdim 375239462Sdim/// \brief Adds an explicit cast to 'id' if the type is not objc object. 376239462Sdimstatic void objectifyExpr(const Expr *E, Commit &commit); 377239462Sdim 378234287Sdimstatic bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 379249423Sdim const NSAPI &NS, Commit &commit, 380249423Sdim const ParentMap *PMap) { 381249423Sdim if (PMap) { 382249423Sdim const ObjCMessageExpr *ParentMsg = 383249423Sdim dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); 384249423Sdim if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) 385249423Sdim return false; 386249423Sdim } 387249423Sdim 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; 401239462Sdim 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 408239462Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 409239462Sdim 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 416239462Sdim for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 417239462Sdim objectifyExpr(Msg->getArg(i), commit); 418239462Sdim 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 437249423Sdim/// \brief If \c Msg is an NSArray creation message or literal, this gets the 438249423Sdim/// objects that were used to create it. 439249423Sdim/// \returns true if it is an NSArray and we got objects, or false otherwise. 440249423Sdimstatic bool getNSArrayObjects(const Expr *E, const NSAPI &NS, 441249423Sdim SmallVectorImpl<const Expr *> &Objs) { 442249423Sdim if (!E) 443249423Sdim return false; 444249423Sdim 445249423Sdim E = E->IgnoreParenCasts(); 446249423Sdim if (!E) 447249423Sdim return false; 448249423Sdim 449249423Sdim if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { 450249423Sdim IdentifierInfo *Cls = 0; 451249423Sdim if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) 452249423Sdim return false; 453249423Sdim 454249423Sdim if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) 455249423Sdim return false; 456249423Sdim 457249423Sdim Selector Sel = Msg->getSelector(); 458249423Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) 459249423Sdim return true; // empty array. 460249423Sdim 461249423Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 462249423Sdim if (Msg->getNumArgs() != 1) 463249423Sdim return false; 464249423Sdim Objs.push_back(Msg->getArg(0)); 465249423Sdim return true; 466249423Sdim } 467249423Sdim 468249423Sdim if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 469249423Sdim Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 470249423Sdim if (Msg->getNumArgs() == 0) 471249423Sdim return false; 472249423Sdim const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 473249423Sdim if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 474249423Sdim return false; 475249423Sdim 476249423Sdim for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 477249423Sdim Objs.push_back(Msg->getArg(i)); 478249423Sdim return true; 479249423Sdim } 480249423Sdim 481249423Sdim } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { 482249423Sdim for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) 483249423Sdim Objs.push_back(ArrLit->getElement(i)); 484249423Sdim return true; 485249423Sdim } 486249423Sdim 487249423Sdim return false; 488249423Sdim} 489249423Sdim 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; 506239462Sdim 507239462Sdim objectifyExpr(Msg->getArg(0), commit); 508239462Sdim objectifyExpr(Msg->getArg(1), commit); 509239462Sdim 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( 524239462Sdim NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 525239462Sdim 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) { 539239462Sdim objectifyExpr(Msg->getArg(i), commit); 540239462Sdim objectifyExpr(Msg->getArg(i+1), commit); 541239462Sdim 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 560249423Sdim if (Sel == NS.getNSDictionarySelector( 561249423Sdim NSAPI::NSDict_dictionaryWithObjectsForKeys) || 562249423Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 563249423Sdim if (Msg->getNumArgs() != 2) 564249423Sdim return false; 565249423Sdim 566249423Sdim SmallVector<const Expr *, 8> Vals; 567249423Sdim if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 568249423Sdim return false; 569249423Sdim 570249423Sdim SmallVector<const Expr *, 8> Keys; 571249423Sdim if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 572249423Sdim return false; 573249423Sdim 574249423Sdim if (Vals.size() != Keys.size()) 575249423Sdim return false; 576249423Sdim 577249423Sdim if (Vals.empty()) { 578249423Sdim commit.replace(MsgRange, "@{}"); 579249423Sdim return true; 580249423Sdim } 581249423Sdim 582249423Sdim for (unsigned i = 0, n = Vals.size(); i < n; ++i) { 583249423Sdim objectifyExpr(Vals[i], commit); 584249423Sdim objectifyExpr(Keys[i], commit); 585249423Sdim 586249423Sdim SourceRange ValRange = Vals[i]->getSourceRange(); 587249423Sdim SourceRange KeyRange = Keys[i]->getSourceRange(); 588249423Sdim // Insert value after key. 589249423Sdim commit.insertAfterToken(KeyRange.getEnd(), ": "); 590249423Sdim commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 591249423Sdim } 592249423Sdim // Range of arguments up until and including the last key. 593249423Sdim // The first value is cut off, the value will move after the key. 594249423Sdim SourceRange ArgRange(Keys.front()->getLocStart(), 595249423Sdim Keys.back()->getLocEnd()); 596249423Sdim commit.insertWrap("@{", ArgRange, "}"); 597249423Sdim commit.replaceWithInner(MsgRange, ArgRange); 598249423Sdim return true; 599249423Sdim } 600249423Sdim 601234287Sdim return false; 602234287Sdim} 603234287Sdim 604249423Sdimstatic bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 605249423Sdim const NSAPI &NS) { 606249423Sdim if (!Msg) 607249423Sdim return false; 608249423Sdim 609249423Sdim IdentifierInfo *II = 0; 610249423Sdim if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 611249423Sdim return false; 612249423Sdim 613249423Sdim if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 614249423Sdim return false; 615249423Sdim 616249423Sdim Selector Sel = Msg->getSelector(); 617249423Sdim if (Sel == NS.getNSDictionarySelector( 618249423Sdim NSAPI::NSDict_dictionaryWithObjectsForKeys) || 619249423Sdim Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 620249423Sdim if (Msg->getNumArgs() != 2) 621249423Sdim return false; 622249423Sdim 623249423Sdim SmallVector<const Expr *, 8> Vals; 624249423Sdim if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 625249423Sdim return false; 626249423Sdim 627249423Sdim SmallVector<const Expr *, 8> Keys; 628249423Sdim if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 629249423Sdim return false; 630249423Sdim 631249423Sdim if (Vals.size() != Keys.size()) 632249423Sdim return false; 633249423Sdim 634249423Sdim return true; 635249423Sdim } 636249423Sdim 637249423Sdim return false; 638249423Sdim} 639249423Sdim 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 657239462Sdim 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 671239462Sdim 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 696249423Sdim 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 773239462Sdim // Only integer and floating literals, otherwise try to rewrite to boxed 774239462Sdim // expression. 775234287Sdim if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 776239462Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 777234287Sdim 778234287Sdim ASTContext &Ctx = NS.getASTContext(); 779234287Sdim Selector Sel = Msg->getSelector(); 780249423Sdim 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: 796239462Sdim 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. 836239462Sdim // Try with boxed expression if it came from a macro. 837234287Sdim if (ArgRange.getBegin().isMacroID()) 838239462Sdim return rewriteToNumericBoxedExpression(Msg, NS, commit); 839234287Sdim 840234287Sdim bool LitIsFloat = ArgTy->isFloatingType(); 841239462Sdim // For a float passed to integer call, don't try rewriting to objc literal. 842239462Sdim // It is difficult and a very uncommon case anyway. 843239462Sdim // But try with boxed expression. 844234287Sdim if (LitIsFloat && !CallIsFloating) 845239462Sdim 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)) 856239462Sdim 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)) 860239462Sdim 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} 886239462Sdim 887239462Sdim// FIXME: Make determination of operator precedence more general and 888239462Sdim// make it broadly available. 889239462Sdimstatic bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 890239462Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 891239462Sdim if (isa<ArraySubscriptExpr>(Expr) || 892239462Sdim isa<CallExpr>(Expr) || 893239462Sdim isa<DeclRefExpr>(Expr) || 894239462Sdim isa<CXXNamedCastExpr>(Expr) || 895239462Sdim isa<CXXConstructExpr>(Expr) || 896239462Sdim isa<CXXThisExpr>(Expr) || 897239462Sdim isa<CXXTypeidExpr>(Expr) || 898239462Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 899239462Sdim isa<ObjCMessageExpr>(Expr) || 900239462Sdim isa<ObjCPropertyRefExpr>(Expr) || 901239462Sdim isa<ObjCProtocolExpr>(Expr) || 902239462Sdim isa<MemberExpr>(Expr) || 903239462Sdim isa<ObjCIvarRefExpr>(Expr) || 904239462Sdim isa<ParenExpr>(FullExpr) || 905239462Sdim isa<ParenListExpr>(Expr) || 906239462Sdim isa<SizeOfPackExpr>(Expr)) 907239462Sdim return false; 908239462Sdim 909239462Sdim return true; 910239462Sdim} 911239462Sdimstatic bool castOperatorNeedsParens(const Expr *FullExpr) { 912239462Sdim const Expr* Expr = FullExpr->IgnoreImpCasts(); 913239462Sdim if (isa<ArraySubscriptExpr>(Expr) || 914239462Sdim isa<CallExpr>(Expr) || 915239462Sdim isa<DeclRefExpr>(Expr) || 916239462Sdim isa<CastExpr>(Expr) || 917239462Sdim isa<CXXNewExpr>(Expr) || 918239462Sdim isa<CXXConstructExpr>(Expr) || 919239462Sdim isa<CXXDeleteExpr>(Expr) || 920239462Sdim isa<CXXNoexceptExpr>(Expr) || 921239462Sdim isa<CXXPseudoDestructorExpr>(Expr) || 922239462Sdim isa<CXXScalarValueInitExpr>(Expr) || 923239462Sdim isa<CXXThisExpr>(Expr) || 924239462Sdim isa<CXXTypeidExpr>(Expr) || 925239462Sdim isa<CXXUnresolvedConstructExpr>(Expr) || 926239462Sdim isa<ObjCMessageExpr>(Expr) || 927239462Sdim isa<ObjCPropertyRefExpr>(Expr) || 928239462Sdim isa<ObjCProtocolExpr>(Expr) || 929239462Sdim isa<MemberExpr>(Expr) || 930239462Sdim isa<ObjCIvarRefExpr>(Expr) || 931239462Sdim isa<ParenExpr>(FullExpr) || 932239462Sdim isa<ParenListExpr>(Expr) || 933239462Sdim isa<SizeOfPackExpr>(Expr) || 934239462Sdim isa<UnaryOperator>(Expr)) 935239462Sdim return false; 936239462Sdim 937239462Sdim return true; 938239462Sdim} 939239462Sdim 940239462Sdimstatic void objectifyExpr(const Expr *E, Commit &commit) { 941239462Sdim if (!E) return; 942239462Sdim 943239462Sdim QualType T = E->getType(); 944239462Sdim if (T->isObjCObjectPointerType()) { 945239462Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 946239462Sdim if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 947239462Sdim return; 948239462Sdim } else { 949239462Sdim return; 950239462Sdim } 951239462Sdim } else if (!T->isPointerType()) { 952239462Sdim return; 953239462Sdim } 954239462Sdim 955239462Sdim SourceRange Range = E->getSourceRange(); 956239462Sdim if (castOperatorNeedsParens(E)) 957239462Sdim commit.insertWrap("(", Range, ")"); 958239462Sdim commit.insertBefore(Range.getBegin(), "(id)"); 959239462Sdim} 960239462Sdim 961239462Sdim//===----------------------------------------------------------------------===// 962239462Sdim// rewriteToNumericBoxedExpression. 963239462Sdim//===----------------------------------------------------------------------===// 964239462Sdim 965239462Sdimstatic bool isEnumConstant(const Expr *E) { 966239462Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 967239462Sdim if (const ValueDecl *VD = DRE->getDecl()) 968239462Sdim return isa<EnumConstantDecl>(VD); 969239462Sdim 970239462Sdim return false; 971239462Sdim} 972239462Sdim 973239462Sdimstatic bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 974239462Sdim const NSAPI &NS, Commit &commit) { 975239462Sdim if (Msg->getNumArgs() != 1) 976239462Sdim return false; 977239462Sdim 978239462Sdim const Expr *Arg = Msg->getArg(0); 979239462Sdim if (Arg->isTypeDependent()) 980239462Sdim return false; 981239462Sdim 982239462Sdim ASTContext &Ctx = NS.getASTContext(); 983239462Sdim Selector Sel = Msg->getSelector(); 984249423Sdim Optional<NSAPI::NSNumberLiteralMethodKind> 985239462Sdim MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 986239462Sdim if (!MKOpt) 987239462Sdim return false; 988239462Sdim NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 989239462Sdim 990239462Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 991239462Sdim QualType FinalTy = Arg->getType(); 992239462Sdim QualType OrigTy = OrigArg->getType(); 993239462Sdim uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 994239462Sdim uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 995239462Sdim 996239462Sdim bool isTruncated = FinalTySize < OrigTySize; 997239462Sdim bool needsCast = false; 998239462Sdim 999239462Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 1000239462Sdim switch (ICE->getCastKind()) { 1001239462Sdim case CK_LValueToRValue: 1002239462Sdim case CK_NoOp: 1003239462Sdim case CK_UserDefinedConversion: 1004239462Sdim break; 1005239462Sdim 1006239462Sdim case CK_IntegralCast: { 1007239462Sdim if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 1008239462Sdim break; 1009239462Sdim // Be more liberal with Integer/UnsignedInteger which are very commonly 1010239462Sdim // used. 1011239462Sdim if ((MK == NSAPI::NSNumberWithInteger || 1012239462Sdim MK == NSAPI::NSNumberWithUnsignedInteger) && 1013239462Sdim !isTruncated) { 1014239462Sdim if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 1015239462Sdim break; 1016239462Sdim if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 1017239462Sdim OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 1018239462Sdim break; 1019239462Sdim } 1020239462Sdim 1021239462Sdim needsCast = true; 1022239462Sdim break; 1023239462Sdim } 1024239462Sdim 1025239462Sdim case CK_PointerToBoolean: 1026239462Sdim case CK_IntegralToBoolean: 1027239462Sdim case CK_IntegralToFloating: 1028239462Sdim case CK_FloatingToIntegral: 1029239462Sdim case CK_FloatingToBoolean: 1030239462Sdim case CK_FloatingCast: 1031239462Sdim case CK_FloatingComplexToReal: 1032239462Sdim case CK_FloatingComplexToBoolean: 1033239462Sdim case CK_IntegralComplexToReal: 1034239462Sdim case CK_IntegralComplexToBoolean: 1035239462Sdim case CK_AtomicToNonAtomic: 1036239462Sdim needsCast = true; 1037239462Sdim break; 1038239462Sdim 1039239462Sdim case CK_Dependent: 1040239462Sdim case CK_BitCast: 1041239462Sdim case CK_LValueBitCast: 1042239462Sdim case CK_BaseToDerived: 1043239462Sdim case CK_DerivedToBase: 1044239462Sdim case CK_UncheckedDerivedToBase: 1045239462Sdim case CK_Dynamic: 1046239462Sdim case CK_ToUnion: 1047239462Sdim case CK_ArrayToPointerDecay: 1048239462Sdim case CK_FunctionToPointerDecay: 1049239462Sdim case CK_NullToPointer: 1050239462Sdim case CK_NullToMemberPointer: 1051239462Sdim case CK_BaseToDerivedMemberPointer: 1052239462Sdim case CK_DerivedToBaseMemberPointer: 1053239462Sdim case CK_MemberPointerToBoolean: 1054239462Sdim case CK_ReinterpretMemberPointer: 1055239462Sdim case CK_ConstructorConversion: 1056239462Sdim case CK_IntegralToPointer: 1057239462Sdim case CK_PointerToIntegral: 1058239462Sdim case CK_ToVoid: 1059239462Sdim case CK_VectorSplat: 1060239462Sdim case CK_CPointerToObjCPointerCast: 1061239462Sdim case CK_BlockPointerToObjCPointerCast: 1062239462Sdim case CK_AnyPointerToBlockPointerCast: 1063239462Sdim case CK_ObjCObjectLValueCast: 1064239462Sdim case CK_FloatingRealToComplex: 1065239462Sdim case CK_FloatingComplexCast: 1066239462Sdim case CK_FloatingComplexToIntegralComplex: 1067239462Sdim case CK_IntegralRealToComplex: 1068239462Sdim case CK_IntegralComplexCast: 1069239462Sdim case CK_IntegralComplexToFloatingComplex: 1070239462Sdim case CK_ARCProduceObject: 1071239462Sdim case CK_ARCConsumeObject: 1072239462Sdim case CK_ARCReclaimReturnedObject: 1073239462Sdim case CK_ARCExtendBlockObject: 1074239462Sdim case CK_NonAtomicToAtomic: 1075239462Sdim case CK_CopyAndAutoreleaseBlockObject: 1076243830Sdim case CK_BuiltinFnToFnPtr: 1077249423Sdim case CK_ZeroToOCLEvent: 1078239462Sdim return false; 1079239462Sdim } 1080239462Sdim } 1081239462Sdim 1082239462Sdim if (needsCast) { 1083239462Sdim DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 1084239462Sdim // FIXME: Use a custom category name to distinguish migration diagnostics. 1085239462Sdim unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 1086239462Sdim "converting to boxing syntax requires casting %0 to %1"); 1087239462Sdim Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 1088239462Sdim << Msg->getSourceRange(); 1089239462Sdim return false; 1090239462Sdim } 1091239462Sdim 1092239462Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 1093239462Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1094239462Sdim 1095239462Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1096239462Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 1097239462Sdim else 1098239462Sdim commit.insertWrap("@(", ArgRange, ")"); 1099239462Sdim 1100239462Sdim return true; 1101239462Sdim} 1102239462Sdim 1103239462Sdim//===----------------------------------------------------------------------===// 1104239462Sdim// rewriteToStringBoxedExpression. 1105239462Sdim//===----------------------------------------------------------------------===// 1106239462Sdim 1107239462Sdimstatic bool doRewriteToUTF8StringBoxedExpressionHelper( 1108239462Sdim const ObjCMessageExpr *Msg, 1109239462Sdim const NSAPI &NS, Commit &commit) { 1110239462Sdim const Expr *Arg = Msg->getArg(0); 1111239462Sdim if (Arg->isTypeDependent()) 1112239462Sdim return false; 1113239462Sdim 1114239462Sdim ASTContext &Ctx = NS.getASTContext(); 1115239462Sdim 1116239462Sdim const Expr *OrigArg = Arg->IgnoreImpCasts(); 1117239462Sdim QualType OrigTy = OrigArg->getType(); 1118239462Sdim if (OrigTy->isArrayType()) 1119239462Sdim OrigTy = Ctx.getArrayDecayedType(OrigTy); 1120239462Sdim 1121239462Sdim if (const StringLiteral * 1122239462Sdim StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 1123239462Sdim commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 1124239462Sdim commit.insert(StrE->getLocStart(), "@"); 1125239462Sdim return true; 1126239462Sdim } 1127239462Sdim 1128239462Sdim if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 1129239462Sdim QualType PointeeType = PT->getPointeeType(); 1130239462Sdim if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 1131239462Sdim SourceRange ArgRange = OrigArg->getSourceRange(); 1132239462Sdim commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1133239462Sdim 1134239462Sdim if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1135239462Sdim commit.insertBefore(ArgRange.getBegin(), "@"); 1136239462Sdim else 1137239462Sdim commit.insertWrap("@(", ArgRange, ")"); 1138239462Sdim 1139239462Sdim return true; 1140239462Sdim } 1141239462Sdim } 1142239462Sdim 1143239462Sdim return false; 1144239462Sdim} 1145239462Sdim 1146239462Sdimstatic bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 1147239462Sdim const NSAPI &NS, Commit &commit) { 1148239462Sdim Selector Sel = Msg->getSelector(); 1149239462Sdim 1150239462Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 1151239462Sdim Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 1152239462Sdim if (Msg->getNumArgs() != 1) 1153239462Sdim return false; 1154239462Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1155239462Sdim } 1156239462Sdim 1157239462Sdim if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1158239462Sdim if (Msg->getNumArgs() != 2) 1159239462Sdim return false; 1160239462Sdim 1161239462Sdim const Expr *encodingArg = Msg->getArg(1); 1162239462Sdim if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1163239462Sdim NS.isNSASCIIStringEncodingConstant(encodingArg)) 1164239462Sdim return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1165239462Sdim } 1166239462Sdim 1167239462Sdim return false; 1168239462Sdim} 1169