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