RewriteObjCFoundationAPI.cpp revision afcb16f9cff8d5ccd57a6386d65ddc055368d0bc
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 23using namespace clang; 24using namespace edit; 25 26static 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 59bool 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'. 108static const ObjCInterfaceDecl * 109maybeAdjustInterfaceForSubscriptingCheck(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 156static 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 172static bool subscriptOperatorNeedsParens(const Expr *FullExpr); 173 174static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 175 if (subscriptOperatorNeedsParens(Receiver)) { 176 SourceRange RecRange = Receiver->getSourceRange(); 177 commit.insertWrap("(", RecRange, ")"); 178 } 179} 180 181static 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 203static 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 213static 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 223static 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 257static 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 290bool 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 *IFace = 300 NS.getASTContext().getObjContainingInterface(Method); 301 if (!IFace) 302 return false; 303 Selector Sel = Msg->getSelector(); 304 305 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 306 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 307 308 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 309 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 310 311 if (Msg->getNumArgs() != 2) 312 return false; 313 314 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 315 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 316 317 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 318 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 319 320 return false; 321} 322 323//===----------------------------------------------------------------------===// 324// rewriteToObjCLiteralSyntax. 325//===----------------------------------------------------------------------===// 326 327static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 328 const NSAPI &NS, Commit &commit, 329 const ParentMap *PMap); 330static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 331 const NSAPI &NS, Commit &commit); 332static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 333 const NSAPI &NS, Commit &commit); 334static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 335 const NSAPI &NS, Commit &commit); 336static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 337 const NSAPI &NS, Commit &commit); 338 339bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 340 const NSAPI &NS, Commit &commit, 341 const ParentMap *PMap) { 342 IdentifierInfo *II = 0; 343 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 344 return false; 345 346 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 347 return rewriteToArrayLiteral(Msg, NS, commit, PMap); 348 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 349 return rewriteToDictionaryLiteral(Msg, NS, commit); 350 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 351 return rewriteToNumberLiteral(Msg, NS, commit); 352 if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 353 return rewriteToStringBoxedExpression(Msg, NS, commit); 354 355 return false; 356} 357 358bool edit::rewriteToObjCProperty(const ObjCMethodDecl *Getter, 359 const ObjCMethodDecl *Setter, 360 const NSAPI &NS, Commit &commit) { 361 return false; 362} 363 364/// \brief Returns true if the immediate message arguments of \c Msg should not 365/// be rewritten because it will interfere with the rewrite of the parent 366/// message expression. e.g. 367/// \code 368/// [NSDictionary dictionaryWithObjects: 369/// [NSArray arrayWithObjects:@"1", @"2", nil] 370/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; 371/// \endcode 372/// It will return true for this because we are going to rewrite this directly 373/// to a dictionary literal without any array literals. 374static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 375 const NSAPI &NS); 376 377//===----------------------------------------------------------------------===// 378// rewriteToArrayLiteral. 379//===----------------------------------------------------------------------===// 380 381/// \brief Adds an explicit cast to 'id' if the type is not objc object. 382static void objectifyExpr(const Expr *E, Commit &commit); 383 384static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 385 const NSAPI &NS, Commit &commit, 386 const ParentMap *PMap) { 387 if (PMap) { 388 const ObjCMessageExpr *ParentMsg = 389 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); 390 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) 391 return false; 392 } 393 394 Selector Sel = Msg->getSelector(); 395 SourceRange MsgRange = Msg->getSourceRange(); 396 397 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 398 if (Msg->getNumArgs() != 0) 399 return false; 400 commit.replace(MsgRange, "@[]"); 401 return true; 402 } 403 404 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 405 if (Msg->getNumArgs() != 1) 406 return false; 407 objectifyExpr(Msg->getArg(0), commit); 408 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 409 commit.replaceWithInner(MsgRange, ArgRange); 410 commit.insertWrap("@[", ArgRange, "]"); 411 return true; 412 } 413 414 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 415 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 416 if (Msg->getNumArgs() == 0) 417 return false; 418 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 419 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 420 return false; 421 422 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 423 objectifyExpr(Msg->getArg(i), commit); 424 425 if (Msg->getNumArgs() == 1) { 426 commit.replace(MsgRange, "@[]"); 427 return true; 428 } 429 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 430 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 431 commit.replaceWithInner(MsgRange, ArgRange); 432 commit.insertWrap("@[", ArgRange, "]"); 433 return true; 434 } 435 436 return false; 437} 438 439//===----------------------------------------------------------------------===// 440// rewriteToDictionaryLiteral. 441//===----------------------------------------------------------------------===// 442 443/// \brief If \c Msg is an NSArray creation message or literal, this gets the 444/// objects that were used to create it. 445/// \returns true if it is an NSArray and we got objects, or false otherwise. 446static bool getNSArrayObjects(const Expr *E, const NSAPI &NS, 447 SmallVectorImpl<const Expr *> &Objs) { 448 if (!E) 449 return false; 450 451 E = E->IgnoreParenCasts(); 452 if (!E) 453 return false; 454 455 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { 456 IdentifierInfo *Cls = 0; 457 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) 458 return false; 459 460 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) 461 return false; 462 463 Selector Sel = Msg->getSelector(); 464 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) 465 return true; // empty array. 466 467 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 468 if (Msg->getNumArgs() != 1) 469 return false; 470 Objs.push_back(Msg->getArg(0)); 471 return true; 472 } 473 474 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 475 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 476 if (Msg->getNumArgs() == 0) 477 return false; 478 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 479 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 480 return false; 481 482 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 483 Objs.push_back(Msg->getArg(i)); 484 return true; 485 } 486 487 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { 488 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) 489 Objs.push_back(ArrLit->getElement(i)); 490 return true; 491 } 492 493 return false; 494} 495 496static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 497 const NSAPI &NS, Commit &commit) { 498 Selector Sel = Msg->getSelector(); 499 SourceRange MsgRange = Msg->getSourceRange(); 500 501 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 502 if (Msg->getNumArgs() != 0) 503 return false; 504 commit.replace(MsgRange, "@{}"); 505 return true; 506 } 507 508 if (Sel == NS.getNSDictionarySelector( 509 NSAPI::NSDict_dictionaryWithObjectForKey)) { 510 if (Msg->getNumArgs() != 2) 511 return false; 512 513 objectifyExpr(Msg->getArg(0), commit); 514 objectifyExpr(Msg->getArg(1), commit); 515 516 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 517 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 518 // Insert key before the value. 519 commit.insertBefore(ValRange.getBegin(), ": "); 520 commit.insertFromRange(ValRange.getBegin(), 521 CharSourceRange::getTokenRange(KeyRange), 522 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 523 commit.insertBefore(ValRange.getBegin(), "@{"); 524 commit.insertAfterToken(ValRange.getEnd(), "}"); 525 commit.replaceWithInner(MsgRange, ValRange); 526 return true; 527 } 528 529 if (Sel == NS.getNSDictionarySelector( 530 NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 531 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 532 if (Msg->getNumArgs() % 2 != 1) 533 return false; 534 unsigned SentinelIdx = Msg->getNumArgs() - 1; 535 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 536 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 537 return false; 538 539 if (Msg->getNumArgs() == 1) { 540 commit.replace(MsgRange, "@{}"); 541 return true; 542 } 543 544 for (unsigned i = 0; i < SentinelIdx; i += 2) { 545 objectifyExpr(Msg->getArg(i), commit); 546 objectifyExpr(Msg->getArg(i+1), commit); 547 548 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 549 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 550 // Insert value after key. 551 commit.insertAfterToken(KeyRange.getEnd(), ": "); 552 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 553 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 554 KeyRange.getBegin())); 555 } 556 // Range of arguments up until and including the last key. 557 // The sentinel and first value are cut off, the value will move after the 558 // key. 559 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 560 Msg->getArg(SentinelIdx-1)->getLocEnd()); 561 commit.insertWrap("@{", ArgRange, "}"); 562 commit.replaceWithInner(MsgRange, ArgRange); 563 return true; 564 } 565 566 if (Sel == NS.getNSDictionarySelector( 567 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 568 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 569 if (Msg->getNumArgs() != 2) 570 return false; 571 572 SmallVector<const Expr *, 8> Vals; 573 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 574 return false; 575 576 SmallVector<const Expr *, 8> Keys; 577 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 578 return false; 579 580 if (Vals.size() != Keys.size()) 581 return false; 582 583 if (Vals.empty()) { 584 commit.replace(MsgRange, "@{}"); 585 return true; 586 } 587 588 for (unsigned i = 0, n = Vals.size(); i < n; ++i) { 589 objectifyExpr(Vals[i], commit); 590 objectifyExpr(Keys[i], commit); 591 592 SourceRange ValRange = Vals[i]->getSourceRange(); 593 SourceRange KeyRange = Keys[i]->getSourceRange(); 594 // Insert value after key. 595 commit.insertAfterToken(KeyRange.getEnd(), ": "); 596 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 597 } 598 // Range of arguments up until and including the last key. 599 // The first value is cut off, the value will move after the key. 600 SourceRange ArgRange(Keys.front()->getLocStart(), 601 Keys.back()->getLocEnd()); 602 commit.insertWrap("@{", ArgRange, "}"); 603 commit.replaceWithInner(MsgRange, ArgRange); 604 return true; 605 } 606 607 return false; 608} 609 610static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 611 const NSAPI &NS) { 612 if (!Msg) 613 return false; 614 615 IdentifierInfo *II = 0; 616 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 617 return false; 618 619 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 620 return false; 621 622 Selector Sel = Msg->getSelector(); 623 if (Sel == NS.getNSDictionarySelector( 624 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 625 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 626 if (Msg->getNumArgs() != 2) 627 return false; 628 629 SmallVector<const Expr *, 8> Vals; 630 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 631 return false; 632 633 SmallVector<const Expr *, 8> Keys; 634 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 635 return false; 636 637 if (Vals.size() != Keys.size()) 638 return false; 639 640 return true; 641 } 642 643 return false; 644} 645 646//===----------------------------------------------------------------------===// 647// rewriteToNumberLiteral. 648//===----------------------------------------------------------------------===// 649 650static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 651 const CharacterLiteral *Arg, 652 const NSAPI &NS, Commit &commit) { 653 if (Arg->getKind() != CharacterLiteral::Ascii) 654 return false; 655 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 656 Msg->getSelector())) { 657 SourceRange ArgRange = Arg->getSourceRange(); 658 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 659 commit.insert(ArgRange.getBegin(), "@"); 660 return true; 661 } 662 663 return rewriteToNumericBoxedExpression(Msg, NS, commit); 664} 665 666static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 667 const Expr *Arg, 668 const NSAPI &NS, Commit &commit) { 669 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 670 Msg->getSelector())) { 671 SourceRange ArgRange = Arg->getSourceRange(); 672 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 673 commit.insert(ArgRange.getBegin(), "@"); 674 return true; 675 } 676 677 return rewriteToNumericBoxedExpression(Msg, NS, commit); 678} 679 680namespace { 681 682struct LiteralInfo { 683 bool Hex, Octal; 684 StringRef U, F, L, LL; 685 CharSourceRange WithoutSuffRange; 686}; 687 688} 689 690static bool getLiteralInfo(SourceRange literalRange, 691 bool isFloat, bool isIntZero, 692 ASTContext &Ctx, LiteralInfo &Info) { 693 if (literalRange.getBegin().isMacroID() || 694 literalRange.getEnd().isMacroID()) 695 return false; 696 StringRef text = Lexer::getSourceText( 697 CharSourceRange::getTokenRange(literalRange), 698 Ctx.getSourceManager(), Ctx.getLangOpts()); 699 if (text.empty()) 700 return false; 701 702 Optional<bool> UpperU, UpperL; 703 bool UpperF = false; 704 705 struct Suff { 706 static bool has(StringRef suff, StringRef &text) { 707 if (text.endswith(suff)) { 708 text = text.substr(0, text.size()-suff.size()); 709 return true; 710 } 711 return false; 712 } 713 }; 714 715 while (1) { 716 if (Suff::has("u", text)) { 717 UpperU = false; 718 } else if (Suff::has("U", text)) { 719 UpperU = true; 720 } else if (Suff::has("ll", text)) { 721 UpperL = false; 722 } else if (Suff::has("LL", text)) { 723 UpperL = true; 724 } else if (Suff::has("l", text)) { 725 UpperL = false; 726 } else if (Suff::has("L", text)) { 727 UpperL = true; 728 } else if (isFloat && Suff::has("f", text)) { 729 UpperF = false; 730 } else if (isFloat && Suff::has("F", text)) { 731 UpperF = true; 732 } else 733 break; 734 } 735 736 if (!UpperU.hasValue() && !UpperL.hasValue()) 737 UpperU = UpperL = true; 738 else if (UpperU.hasValue() && !UpperL.hasValue()) 739 UpperL = UpperU; 740 else if (UpperL.hasValue() && !UpperU.hasValue()) 741 UpperU = UpperL; 742 743 Info.U = *UpperU ? "U" : "u"; 744 Info.L = *UpperL ? "L" : "l"; 745 Info.LL = *UpperL ? "LL" : "ll"; 746 Info.F = UpperF ? "F" : "f"; 747 748 Info.Hex = Info.Octal = false; 749 if (text.startswith("0x")) 750 Info.Hex = true; 751 else if (!isFloat && !isIntZero && text.startswith("0")) 752 Info.Octal = true; 753 754 SourceLocation B = literalRange.getBegin(); 755 Info.WithoutSuffRange = 756 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 757 return true; 758} 759 760static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 761 const NSAPI &NS, Commit &commit) { 762 if (Msg->getNumArgs() != 1) 763 return false; 764 765 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 766 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 767 return rewriteToCharLiteral(Msg, CharE, NS, commit); 768 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 769 return rewriteToBoolLiteral(Msg, BE, NS, commit); 770 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 771 return rewriteToBoolLiteral(Msg, BE, NS, commit); 772 773 const Expr *literalE = Arg; 774 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 775 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 776 literalE = UOE->getSubExpr(); 777 } 778 779 // Only integer and floating literals, otherwise try to rewrite to boxed 780 // expression. 781 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 782 return rewriteToNumericBoxedExpression(Msg, NS, commit); 783 784 ASTContext &Ctx = NS.getASTContext(); 785 Selector Sel = Msg->getSelector(); 786 Optional<NSAPI::NSNumberLiteralMethodKind> 787 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 788 if (!MKOpt) 789 return false; 790 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 791 792 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 793 bool CallIsFloating = false, CallIsDouble = false; 794 795 switch (MK) { 796 // We cannot have these calls with int/float literals. 797 case NSAPI::NSNumberWithChar: 798 case NSAPI::NSNumberWithUnsignedChar: 799 case NSAPI::NSNumberWithShort: 800 case NSAPI::NSNumberWithUnsignedShort: 801 case NSAPI::NSNumberWithBool: 802 return rewriteToNumericBoxedExpression(Msg, NS, commit); 803 804 case NSAPI::NSNumberWithUnsignedInt: 805 case NSAPI::NSNumberWithUnsignedInteger: 806 CallIsUnsigned = true; 807 case NSAPI::NSNumberWithInt: 808 case NSAPI::NSNumberWithInteger: 809 break; 810 811 case NSAPI::NSNumberWithUnsignedLong: 812 CallIsUnsigned = true; 813 case NSAPI::NSNumberWithLong: 814 CallIsLong = true; 815 break; 816 817 case NSAPI::NSNumberWithUnsignedLongLong: 818 CallIsUnsigned = true; 819 case NSAPI::NSNumberWithLongLong: 820 CallIsLongLong = true; 821 break; 822 823 case NSAPI::NSNumberWithDouble: 824 CallIsDouble = true; 825 case NSAPI::NSNumberWithFloat: 826 CallIsFloating = true; 827 break; 828 } 829 830 SourceRange ArgRange = Arg->getSourceRange(); 831 QualType ArgTy = Arg->getType(); 832 QualType CallTy = Msg->getArg(0)->getType(); 833 834 // Check for the easy case, the literal maps directly to the call. 835 if (Ctx.hasSameType(ArgTy, CallTy)) { 836 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 837 commit.insert(ArgRange.getBegin(), "@"); 838 return true; 839 } 840 841 // We will need to modify the literal suffix to get the same type as the call. 842 // Try with boxed expression if it came from a macro. 843 if (ArgRange.getBegin().isMacroID()) 844 return rewriteToNumericBoxedExpression(Msg, NS, commit); 845 846 bool LitIsFloat = ArgTy->isFloatingType(); 847 // For a float passed to integer call, don't try rewriting to objc literal. 848 // It is difficult and a very uncommon case anyway. 849 // But try with boxed expression. 850 if (LitIsFloat && !CallIsFloating) 851 return rewriteToNumericBoxedExpression(Msg, NS, commit); 852 853 // Try to modify the literal make it the same type as the method call. 854 // -Modify the suffix, and/or 855 // -Change integer to float 856 857 LiteralInfo LitInfo; 858 bool isIntZero = false; 859 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 860 isIntZero = !IntE->getValue().getBoolValue(); 861 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 862 return rewriteToNumericBoxedExpression(Msg, NS, commit); 863 864 // Not easy to do int -> float with hex/octal and uncommon anyway. 865 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 866 return rewriteToNumericBoxedExpression(Msg, NS, commit); 867 868 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 869 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 870 871 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 872 LitInfo.WithoutSuffRange); 873 commit.insert(LitB, "@"); 874 875 if (!LitIsFloat && CallIsFloating) 876 commit.insert(LitE, ".0"); 877 878 if (CallIsFloating) { 879 if (!CallIsDouble) 880 commit.insert(LitE, LitInfo.F); 881 } else { 882 if (CallIsUnsigned) 883 commit.insert(LitE, LitInfo.U); 884 885 if (CallIsLong) 886 commit.insert(LitE, LitInfo.L); 887 else if (CallIsLongLong) 888 commit.insert(LitE, LitInfo.LL); 889 } 890 return true; 891} 892 893// FIXME: Make determination of operator precedence more general and 894// make it broadly available. 895static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 896 const Expr* Expr = FullExpr->IgnoreImpCasts(); 897 if (isa<ArraySubscriptExpr>(Expr) || 898 isa<CallExpr>(Expr) || 899 isa<DeclRefExpr>(Expr) || 900 isa<CXXNamedCastExpr>(Expr) || 901 isa<CXXConstructExpr>(Expr) || 902 isa<CXXThisExpr>(Expr) || 903 isa<CXXTypeidExpr>(Expr) || 904 isa<CXXUnresolvedConstructExpr>(Expr) || 905 isa<ObjCMessageExpr>(Expr) || 906 isa<ObjCPropertyRefExpr>(Expr) || 907 isa<ObjCProtocolExpr>(Expr) || 908 isa<MemberExpr>(Expr) || 909 isa<ObjCIvarRefExpr>(Expr) || 910 isa<ParenExpr>(FullExpr) || 911 isa<ParenListExpr>(Expr) || 912 isa<SizeOfPackExpr>(Expr)) 913 return false; 914 915 return true; 916} 917static bool castOperatorNeedsParens(const Expr *FullExpr) { 918 const Expr* Expr = FullExpr->IgnoreImpCasts(); 919 if (isa<ArraySubscriptExpr>(Expr) || 920 isa<CallExpr>(Expr) || 921 isa<DeclRefExpr>(Expr) || 922 isa<CastExpr>(Expr) || 923 isa<CXXNewExpr>(Expr) || 924 isa<CXXConstructExpr>(Expr) || 925 isa<CXXDeleteExpr>(Expr) || 926 isa<CXXNoexceptExpr>(Expr) || 927 isa<CXXPseudoDestructorExpr>(Expr) || 928 isa<CXXScalarValueInitExpr>(Expr) || 929 isa<CXXThisExpr>(Expr) || 930 isa<CXXTypeidExpr>(Expr) || 931 isa<CXXUnresolvedConstructExpr>(Expr) || 932 isa<ObjCMessageExpr>(Expr) || 933 isa<ObjCPropertyRefExpr>(Expr) || 934 isa<ObjCProtocolExpr>(Expr) || 935 isa<MemberExpr>(Expr) || 936 isa<ObjCIvarRefExpr>(Expr) || 937 isa<ParenExpr>(FullExpr) || 938 isa<ParenListExpr>(Expr) || 939 isa<SizeOfPackExpr>(Expr) || 940 isa<UnaryOperator>(Expr)) 941 return false; 942 943 return true; 944} 945 946static void objectifyExpr(const Expr *E, Commit &commit) { 947 if (!E) return; 948 949 QualType T = E->getType(); 950 if (T->isObjCObjectPointerType()) { 951 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 952 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 953 return; 954 } else { 955 return; 956 } 957 } else if (!T->isPointerType()) { 958 return; 959 } 960 961 SourceRange Range = E->getSourceRange(); 962 if (castOperatorNeedsParens(E)) 963 commit.insertWrap("(", Range, ")"); 964 commit.insertBefore(Range.getBegin(), "(id)"); 965} 966 967//===----------------------------------------------------------------------===// 968// rewriteToNumericBoxedExpression. 969//===----------------------------------------------------------------------===// 970 971static bool isEnumConstant(const Expr *E) { 972 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 973 if (const ValueDecl *VD = DRE->getDecl()) 974 return isa<EnumConstantDecl>(VD); 975 976 return false; 977} 978 979static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 980 const NSAPI &NS, Commit &commit) { 981 if (Msg->getNumArgs() != 1) 982 return false; 983 984 const Expr *Arg = Msg->getArg(0); 985 if (Arg->isTypeDependent()) 986 return false; 987 988 ASTContext &Ctx = NS.getASTContext(); 989 Selector Sel = Msg->getSelector(); 990 Optional<NSAPI::NSNumberLiteralMethodKind> 991 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 992 if (!MKOpt) 993 return false; 994 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 995 996 const Expr *OrigArg = Arg->IgnoreImpCasts(); 997 QualType FinalTy = Arg->getType(); 998 QualType OrigTy = OrigArg->getType(); 999 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 1000 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 1001 1002 bool isTruncated = FinalTySize < OrigTySize; 1003 bool needsCast = false; 1004 1005 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 1006 switch (ICE->getCastKind()) { 1007 case CK_LValueToRValue: 1008 case CK_NoOp: 1009 case CK_UserDefinedConversion: 1010 break; 1011 1012 case CK_IntegralCast: { 1013 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 1014 break; 1015 // Be more liberal with Integer/UnsignedInteger which are very commonly 1016 // used. 1017 if ((MK == NSAPI::NSNumberWithInteger || 1018 MK == NSAPI::NSNumberWithUnsignedInteger) && 1019 !isTruncated) { 1020 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 1021 break; 1022 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 1023 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 1024 break; 1025 } 1026 1027 needsCast = true; 1028 break; 1029 } 1030 1031 case CK_PointerToBoolean: 1032 case CK_IntegralToBoolean: 1033 case CK_IntegralToFloating: 1034 case CK_FloatingToIntegral: 1035 case CK_FloatingToBoolean: 1036 case CK_FloatingCast: 1037 case CK_FloatingComplexToReal: 1038 case CK_FloatingComplexToBoolean: 1039 case CK_IntegralComplexToReal: 1040 case CK_IntegralComplexToBoolean: 1041 case CK_AtomicToNonAtomic: 1042 needsCast = true; 1043 break; 1044 1045 case CK_Dependent: 1046 case CK_BitCast: 1047 case CK_LValueBitCast: 1048 case CK_BaseToDerived: 1049 case CK_DerivedToBase: 1050 case CK_UncheckedDerivedToBase: 1051 case CK_Dynamic: 1052 case CK_ToUnion: 1053 case CK_ArrayToPointerDecay: 1054 case CK_FunctionToPointerDecay: 1055 case CK_NullToPointer: 1056 case CK_NullToMemberPointer: 1057 case CK_BaseToDerivedMemberPointer: 1058 case CK_DerivedToBaseMemberPointer: 1059 case CK_MemberPointerToBoolean: 1060 case CK_ReinterpretMemberPointer: 1061 case CK_ConstructorConversion: 1062 case CK_IntegralToPointer: 1063 case CK_PointerToIntegral: 1064 case CK_ToVoid: 1065 case CK_VectorSplat: 1066 case CK_CPointerToObjCPointerCast: 1067 case CK_BlockPointerToObjCPointerCast: 1068 case CK_AnyPointerToBlockPointerCast: 1069 case CK_ObjCObjectLValueCast: 1070 case CK_FloatingRealToComplex: 1071 case CK_FloatingComplexCast: 1072 case CK_FloatingComplexToIntegralComplex: 1073 case CK_IntegralRealToComplex: 1074 case CK_IntegralComplexCast: 1075 case CK_IntegralComplexToFloatingComplex: 1076 case CK_ARCProduceObject: 1077 case CK_ARCConsumeObject: 1078 case CK_ARCReclaimReturnedObject: 1079 case CK_ARCExtendBlockObject: 1080 case CK_NonAtomicToAtomic: 1081 case CK_CopyAndAutoreleaseBlockObject: 1082 case CK_BuiltinFnToFnPtr: 1083 case CK_ZeroToOCLEvent: 1084 return false; 1085 } 1086 } 1087 1088 if (needsCast) { 1089 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 1090 // FIXME: Use a custom category name to distinguish migration diagnostics. 1091 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 1092 "converting to boxing syntax requires casting %0 to %1"); 1093 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 1094 << Msg->getSourceRange(); 1095 return false; 1096 } 1097 1098 SourceRange ArgRange = OrigArg->getSourceRange(); 1099 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1100 1101 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1102 commit.insertBefore(ArgRange.getBegin(), "@"); 1103 else 1104 commit.insertWrap("@(", ArgRange, ")"); 1105 1106 return true; 1107} 1108 1109//===----------------------------------------------------------------------===// 1110// rewriteToStringBoxedExpression. 1111//===----------------------------------------------------------------------===// 1112 1113static bool doRewriteToUTF8StringBoxedExpressionHelper( 1114 const ObjCMessageExpr *Msg, 1115 const NSAPI &NS, Commit &commit) { 1116 const Expr *Arg = Msg->getArg(0); 1117 if (Arg->isTypeDependent()) 1118 return false; 1119 1120 ASTContext &Ctx = NS.getASTContext(); 1121 1122 const Expr *OrigArg = Arg->IgnoreImpCasts(); 1123 QualType OrigTy = OrigArg->getType(); 1124 if (OrigTy->isArrayType()) 1125 OrigTy = Ctx.getArrayDecayedType(OrigTy); 1126 1127 if (const StringLiteral * 1128 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 1129 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 1130 commit.insert(StrE->getLocStart(), "@"); 1131 return true; 1132 } 1133 1134 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 1135 QualType PointeeType = PT->getPointeeType(); 1136 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 1137 SourceRange ArgRange = OrigArg->getSourceRange(); 1138 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1139 1140 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1141 commit.insertBefore(ArgRange.getBegin(), "@"); 1142 else 1143 commit.insertWrap("@(", ArgRange, ")"); 1144 1145 return true; 1146 } 1147 } 1148 1149 return false; 1150} 1151 1152static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 1153 const NSAPI &NS, Commit &commit) { 1154 Selector Sel = Msg->getSelector(); 1155 1156 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 1157 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 1158 if (Msg->getNumArgs() != 1) 1159 return false; 1160 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1161 } 1162 1163 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1164 if (Msg->getNumArgs() != 2) 1165 return false; 1166 1167 const Expr *encodingArg = Msg->getArg(1); 1168 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1169 NS.isNSASCIIStringEncodingConstant(encodingArg)) 1170 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1171 } 1172 1173 return false; 1174} 1175