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