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