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/Edit/Commit.h" 16#include "clang/Lex/Lexer.h" 17#include "clang/AST/ASTContext.h" 18#include "clang/AST/ExprObjC.h" 19#include "clang/AST/ExprCXX.h" 20#include "clang/AST/NSAPI.h" 21 22using namespace clang; 23using namespace edit; 24 25static 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 58bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, 59 const NSAPI &NS, Commit &commit) { 60 IdentifierInfo *II = 0; 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/// \brief 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'. 107static const ObjCInterfaceDecl * 108maybeAdjustInterfaceForSubscriptingCheck(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 155static 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 171static bool subscriptOperatorNeedsParens(const Expr *FullExpr); 172 173static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 174 if (subscriptOperatorNeedsParens(Receiver)) { 175 SourceRange RecRange = Receiver->getSourceRange(); 176 commit.insertWrap("(", RecRange, ")"); 177 } 178} 179 180static 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 202static 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 212static 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 222static 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 256static 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 289bool 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 * 299 IFace = NS.getASTContext().getObjContainingInterface( 300 const_cast<ObjCMethodDecl *>(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); 329static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 330 const NSAPI &NS, Commit &commit); 331static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 332 const NSAPI &NS, Commit &commit); 333static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 334 const NSAPI &NS, Commit &commit); 335static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 336 const NSAPI &NS, Commit &commit); 337 338bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 339 const NSAPI &NS, Commit &commit) { 340 IdentifierInfo *II = 0; 341 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 342 return false; 343 344 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 345 return rewriteToArrayLiteral(Msg, NS, commit); 346 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 347 return rewriteToDictionaryLiteral(Msg, NS, commit); 348 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 349 return rewriteToNumberLiteral(Msg, NS, commit); 350 if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 351 return rewriteToStringBoxedExpression(Msg, NS, commit); 352 353 return false; 354} 355 356//===----------------------------------------------------------------------===// 357// rewriteToArrayLiteral. 358//===----------------------------------------------------------------------===// 359 360/// \brief Adds an explicit cast to 'id' if the type is not objc object. 361static void objectifyExpr(const Expr *E, Commit &commit); 362 363static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 364 const NSAPI &NS, Commit &commit) { 365 Selector Sel = Msg->getSelector(); 366 SourceRange MsgRange = Msg->getSourceRange(); 367 368 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 369 if (Msg->getNumArgs() != 0) 370 return false; 371 commit.replace(MsgRange, "@[]"); 372 return true; 373 } 374 375 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 376 if (Msg->getNumArgs() != 1) 377 return false; 378 objectifyExpr(Msg->getArg(0), commit); 379 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 380 commit.replaceWithInner(MsgRange, ArgRange); 381 commit.insertWrap("@[", ArgRange, "]"); 382 return true; 383 } 384 385 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 386 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 387 if (Msg->getNumArgs() == 0) 388 return false; 389 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 390 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 391 return false; 392 393 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 394 objectifyExpr(Msg->getArg(i), commit); 395 396 if (Msg->getNumArgs() == 1) { 397 commit.replace(MsgRange, "@[]"); 398 return true; 399 } 400 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 401 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 402 commit.replaceWithInner(MsgRange, ArgRange); 403 commit.insertWrap("@[", ArgRange, "]"); 404 return true; 405 } 406 407 return false; 408} 409 410//===----------------------------------------------------------------------===// 411// rewriteToDictionaryLiteral. 412//===----------------------------------------------------------------------===// 413 414static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 415 const NSAPI &NS, Commit &commit) { 416 Selector Sel = Msg->getSelector(); 417 SourceRange MsgRange = Msg->getSourceRange(); 418 419 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 420 if (Msg->getNumArgs() != 0) 421 return false; 422 commit.replace(MsgRange, "@{}"); 423 return true; 424 } 425 426 if (Sel == NS.getNSDictionarySelector( 427 NSAPI::NSDict_dictionaryWithObjectForKey)) { 428 if (Msg->getNumArgs() != 2) 429 return false; 430 431 objectifyExpr(Msg->getArg(0), commit); 432 objectifyExpr(Msg->getArg(1), commit); 433 434 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 435 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 436 // Insert key before the value. 437 commit.insertBefore(ValRange.getBegin(), ": "); 438 commit.insertFromRange(ValRange.getBegin(), 439 CharSourceRange::getTokenRange(KeyRange), 440 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 441 commit.insertBefore(ValRange.getBegin(), "@{"); 442 commit.insertAfterToken(ValRange.getEnd(), "}"); 443 commit.replaceWithInner(MsgRange, ValRange); 444 return true; 445 } 446 447 if (Sel == NS.getNSDictionarySelector( 448 NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 449 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 450 if (Msg->getNumArgs() % 2 != 1) 451 return false; 452 unsigned SentinelIdx = Msg->getNumArgs() - 1; 453 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 454 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 455 return false; 456 457 if (Msg->getNumArgs() == 1) { 458 commit.replace(MsgRange, "@{}"); 459 return true; 460 } 461 462 for (unsigned i = 0; i < SentinelIdx; i += 2) { 463 objectifyExpr(Msg->getArg(i), commit); 464 objectifyExpr(Msg->getArg(i+1), commit); 465 466 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 467 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 468 // Insert value after key. 469 commit.insertAfterToken(KeyRange.getEnd(), ": "); 470 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 471 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 472 KeyRange.getBegin())); 473 } 474 // Range of arguments up until and including the last key. 475 // The sentinel and first value are cut off, the value will move after the 476 // key. 477 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 478 Msg->getArg(SentinelIdx-1)->getLocEnd()); 479 commit.insertWrap("@{", ArgRange, "}"); 480 commit.replaceWithInner(MsgRange, ArgRange); 481 return true; 482 } 483 484 return false; 485} 486 487//===----------------------------------------------------------------------===// 488// rewriteToNumberLiteral. 489//===----------------------------------------------------------------------===// 490 491static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 492 const CharacterLiteral *Arg, 493 const NSAPI &NS, Commit &commit) { 494 if (Arg->getKind() != CharacterLiteral::Ascii) 495 return false; 496 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 497 Msg->getSelector())) { 498 SourceRange ArgRange = Arg->getSourceRange(); 499 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 500 commit.insert(ArgRange.getBegin(), "@"); 501 return true; 502 } 503 504 return rewriteToNumericBoxedExpression(Msg, NS, commit); 505} 506 507static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 508 const Expr *Arg, 509 const NSAPI &NS, Commit &commit) { 510 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 511 Msg->getSelector())) { 512 SourceRange ArgRange = Arg->getSourceRange(); 513 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 514 commit.insert(ArgRange.getBegin(), "@"); 515 return true; 516 } 517 518 return rewriteToNumericBoxedExpression(Msg, NS, commit); 519} 520 521namespace { 522 523struct LiteralInfo { 524 bool Hex, Octal; 525 StringRef U, F, L, LL; 526 CharSourceRange WithoutSuffRange; 527}; 528 529} 530 531static bool getLiteralInfo(SourceRange literalRange, 532 bool isFloat, bool isIntZero, 533 ASTContext &Ctx, LiteralInfo &Info) { 534 if (literalRange.getBegin().isMacroID() || 535 literalRange.getEnd().isMacroID()) 536 return false; 537 StringRef text = Lexer::getSourceText( 538 CharSourceRange::getTokenRange(literalRange), 539 Ctx.getSourceManager(), Ctx.getLangOpts()); 540 if (text.empty()) 541 return false; 542 543 llvm::Optional<bool> UpperU, UpperL; 544 bool UpperF = false; 545 546 struct Suff { 547 static bool has(StringRef suff, StringRef &text) { 548 if (text.endswith(suff)) { 549 text = text.substr(0, text.size()-suff.size()); 550 return true; 551 } 552 return false; 553 } 554 }; 555 556 while (1) { 557 if (Suff::has("u", text)) { 558 UpperU = false; 559 } else if (Suff::has("U", text)) { 560 UpperU = true; 561 } else if (Suff::has("ll", text)) { 562 UpperL = false; 563 } else if (Suff::has("LL", text)) { 564 UpperL = true; 565 } else if (Suff::has("l", text)) { 566 UpperL = false; 567 } else if (Suff::has("L", text)) { 568 UpperL = true; 569 } else if (isFloat && Suff::has("f", text)) { 570 UpperF = false; 571 } else if (isFloat && Suff::has("F", text)) { 572 UpperF = true; 573 } else 574 break; 575 } 576 577 if (!UpperU.hasValue() && !UpperL.hasValue()) 578 UpperU = UpperL = true; 579 else if (UpperU.hasValue() && !UpperL.hasValue()) 580 UpperL = UpperU; 581 else if (UpperL.hasValue() && !UpperU.hasValue()) 582 UpperU = UpperL; 583 584 Info.U = *UpperU ? "U" : "u"; 585 Info.L = *UpperL ? "L" : "l"; 586 Info.LL = *UpperL ? "LL" : "ll"; 587 Info.F = UpperF ? "F" : "f"; 588 589 Info.Hex = Info.Octal = false; 590 if (text.startswith("0x")) 591 Info.Hex = true; 592 else if (!isFloat && !isIntZero && text.startswith("0")) 593 Info.Octal = true; 594 595 SourceLocation B = literalRange.getBegin(); 596 Info.WithoutSuffRange = 597 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 598 return true; 599} 600 601static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 602 const NSAPI &NS, Commit &commit) { 603 if (Msg->getNumArgs() != 1) 604 return false; 605 606 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 607 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 608 return rewriteToCharLiteral(Msg, CharE, NS, commit); 609 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 610 return rewriteToBoolLiteral(Msg, BE, NS, commit); 611 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 612 return rewriteToBoolLiteral(Msg, BE, NS, commit); 613 614 const Expr *literalE = Arg; 615 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 616 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 617 literalE = UOE->getSubExpr(); 618 } 619 620 // Only integer and floating literals, otherwise try to rewrite to boxed 621 // expression. 622 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 623 return rewriteToNumericBoxedExpression(Msg, NS, commit); 624 625 ASTContext &Ctx = NS.getASTContext(); 626 Selector Sel = Msg->getSelector(); 627 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 628 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 629 if (!MKOpt) 630 return false; 631 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 632 633 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 634 bool CallIsFloating = false, CallIsDouble = false; 635 636 switch (MK) { 637 // We cannot have these calls with int/float literals. 638 case NSAPI::NSNumberWithChar: 639 case NSAPI::NSNumberWithUnsignedChar: 640 case NSAPI::NSNumberWithShort: 641 case NSAPI::NSNumberWithUnsignedShort: 642 case NSAPI::NSNumberWithBool: 643 return rewriteToNumericBoxedExpression(Msg, NS, commit); 644 645 case NSAPI::NSNumberWithUnsignedInt: 646 case NSAPI::NSNumberWithUnsignedInteger: 647 CallIsUnsigned = true; 648 case NSAPI::NSNumberWithInt: 649 case NSAPI::NSNumberWithInteger: 650 break; 651 652 case NSAPI::NSNumberWithUnsignedLong: 653 CallIsUnsigned = true; 654 case NSAPI::NSNumberWithLong: 655 CallIsLong = true; 656 break; 657 658 case NSAPI::NSNumberWithUnsignedLongLong: 659 CallIsUnsigned = true; 660 case NSAPI::NSNumberWithLongLong: 661 CallIsLongLong = true; 662 break; 663 664 case NSAPI::NSNumberWithDouble: 665 CallIsDouble = true; 666 case NSAPI::NSNumberWithFloat: 667 CallIsFloating = true; 668 break; 669 } 670 671 SourceRange ArgRange = Arg->getSourceRange(); 672 QualType ArgTy = Arg->getType(); 673 QualType CallTy = Msg->getArg(0)->getType(); 674 675 // Check for the easy case, the literal maps directly to the call. 676 if (Ctx.hasSameType(ArgTy, CallTy)) { 677 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 678 commit.insert(ArgRange.getBegin(), "@"); 679 return true; 680 } 681 682 // We will need to modify the literal suffix to get the same type as the call. 683 // Try with boxed expression if it came from a macro. 684 if (ArgRange.getBegin().isMacroID()) 685 return rewriteToNumericBoxedExpression(Msg, NS, commit); 686 687 bool LitIsFloat = ArgTy->isFloatingType(); 688 // For a float passed to integer call, don't try rewriting to objc literal. 689 // It is difficult and a very uncommon case anyway. 690 // But try with boxed expression. 691 if (LitIsFloat && !CallIsFloating) 692 return rewriteToNumericBoxedExpression(Msg, NS, commit); 693 694 // Try to modify the literal make it the same type as the method call. 695 // -Modify the suffix, and/or 696 // -Change integer to float 697 698 LiteralInfo LitInfo; 699 bool isIntZero = false; 700 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 701 isIntZero = !IntE->getValue().getBoolValue(); 702 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 703 return rewriteToNumericBoxedExpression(Msg, NS, commit); 704 705 // Not easy to do int -> float with hex/octal and uncommon anyway. 706 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 707 return rewriteToNumericBoxedExpression(Msg, NS, commit); 708 709 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 710 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 711 712 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 713 LitInfo.WithoutSuffRange); 714 commit.insert(LitB, "@"); 715 716 if (!LitIsFloat && CallIsFloating) 717 commit.insert(LitE, ".0"); 718 719 if (CallIsFloating) { 720 if (!CallIsDouble) 721 commit.insert(LitE, LitInfo.F); 722 } else { 723 if (CallIsUnsigned) 724 commit.insert(LitE, LitInfo.U); 725 726 if (CallIsLong) 727 commit.insert(LitE, LitInfo.L); 728 else if (CallIsLongLong) 729 commit.insert(LitE, LitInfo.LL); 730 } 731 return true; 732} 733 734// FIXME: Make determination of operator precedence more general and 735// make it broadly available. 736static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 737 const Expr* Expr = FullExpr->IgnoreImpCasts(); 738 if (isa<ArraySubscriptExpr>(Expr) || 739 isa<CallExpr>(Expr) || 740 isa<DeclRefExpr>(Expr) || 741 isa<CXXNamedCastExpr>(Expr) || 742 isa<CXXConstructExpr>(Expr) || 743 isa<CXXThisExpr>(Expr) || 744 isa<CXXTypeidExpr>(Expr) || 745 isa<CXXUnresolvedConstructExpr>(Expr) || 746 isa<ObjCMessageExpr>(Expr) || 747 isa<ObjCPropertyRefExpr>(Expr) || 748 isa<ObjCProtocolExpr>(Expr) || 749 isa<MemberExpr>(Expr) || 750 isa<ObjCIvarRefExpr>(Expr) || 751 isa<ParenExpr>(FullExpr) || 752 isa<ParenListExpr>(Expr) || 753 isa<SizeOfPackExpr>(Expr)) 754 return false; 755 756 return true; 757} 758static bool castOperatorNeedsParens(const Expr *FullExpr) { 759 const Expr* Expr = FullExpr->IgnoreImpCasts(); 760 if (isa<ArraySubscriptExpr>(Expr) || 761 isa<CallExpr>(Expr) || 762 isa<DeclRefExpr>(Expr) || 763 isa<CastExpr>(Expr) || 764 isa<CXXNewExpr>(Expr) || 765 isa<CXXConstructExpr>(Expr) || 766 isa<CXXDeleteExpr>(Expr) || 767 isa<CXXNoexceptExpr>(Expr) || 768 isa<CXXPseudoDestructorExpr>(Expr) || 769 isa<CXXScalarValueInitExpr>(Expr) || 770 isa<CXXThisExpr>(Expr) || 771 isa<CXXTypeidExpr>(Expr) || 772 isa<CXXUnresolvedConstructExpr>(Expr) || 773 isa<ObjCMessageExpr>(Expr) || 774 isa<ObjCPropertyRefExpr>(Expr) || 775 isa<ObjCProtocolExpr>(Expr) || 776 isa<MemberExpr>(Expr) || 777 isa<ObjCIvarRefExpr>(Expr) || 778 isa<ParenExpr>(FullExpr) || 779 isa<ParenListExpr>(Expr) || 780 isa<SizeOfPackExpr>(Expr) || 781 isa<UnaryOperator>(Expr)) 782 return false; 783 784 return true; 785} 786 787static void objectifyExpr(const Expr *E, Commit &commit) { 788 if (!E) return; 789 790 QualType T = E->getType(); 791 if (T->isObjCObjectPointerType()) { 792 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 793 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 794 return; 795 } else { 796 return; 797 } 798 } else if (!T->isPointerType()) { 799 return; 800 } 801 802 SourceRange Range = E->getSourceRange(); 803 if (castOperatorNeedsParens(E)) 804 commit.insertWrap("(", Range, ")"); 805 commit.insertBefore(Range.getBegin(), "(id)"); 806} 807 808//===----------------------------------------------------------------------===// 809// rewriteToNumericBoxedExpression. 810//===----------------------------------------------------------------------===// 811 812static bool isEnumConstant(const Expr *E) { 813 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 814 if (const ValueDecl *VD = DRE->getDecl()) 815 return isa<EnumConstantDecl>(VD); 816 817 return false; 818} 819 820static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 821 const NSAPI &NS, Commit &commit) { 822 if (Msg->getNumArgs() != 1) 823 return false; 824 825 const Expr *Arg = Msg->getArg(0); 826 if (Arg->isTypeDependent()) 827 return false; 828 829 ASTContext &Ctx = NS.getASTContext(); 830 Selector Sel = Msg->getSelector(); 831 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 832 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 833 if (!MKOpt) 834 return false; 835 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 836 837 const Expr *OrigArg = Arg->IgnoreImpCasts(); 838 QualType FinalTy = Arg->getType(); 839 QualType OrigTy = OrigArg->getType(); 840 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 841 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 842 843 bool isTruncated = FinalTySize < OrigTySize; 844 bool needsCast = false; 845 846 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 847 switch (ICE->getCastKind()) { 848 case CK_LValueToRValue: 849 case CK_NoOp: 850 case CK_UserDefinedConversion: 851 break; 852 853 case CK_IntegralCast: { 854 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 855 break; 856 // Be more liberal with Integer/UnsignedInteger which are very commonly 857 // used. 858 if ((MK == NSAPI::NSNumberWithInteger || 859 MK == NSAPI::NSNumberWithUnsignedInteger) && 860 !isTruncated) { 861 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 862 break; 863 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 864 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 865 break; 866 } 867 868 needsCast = true; 869 break; 870 } 871 872 case CK_PointerToBoolean: 873 case CK_IntegralToBoolean: 874 case CK_IntegralToFloating: 875 case CK_FloatingToIntegral: 876 case CK_FloatingToBoolean: 877 case CK_FloatingCast: 878 case CK_FloatingComplexToReal: 879 case CK_FloatingComplexToBoolean: 880 case CK_IntegralComplexToReal: 881 case CK_IntegralComplexToBoolean: 882 case CK_AtomicToNonAtomic: 883 needsCast = true; 884 break; 885 886 case CK_Dependent: 887 case CK_BitCast: 888 case CK_LValueBitCast: 889 case CK_BaseToDerived: 890 case CK_DerivedToBase: 891 case CK_UncheckedDerivedToBase: 892 case CK_Dynamic: 893 case CK_ToUnion: 894 case CK_ArrayToPointerDecay: 895 case CK_FunctionToPointerDecay: 896 case CK_NullToPointer: 897 case CK_NullToMemberPointer: 898 case CK_BaseToDerivedMemberPointer: 899 case CK_DerivedToBaseMemberPointer: 900 case CK_MemberPointerToBoolean: 901 case CK_ReinterpretMemberPointer: 902 case CK_ConstructorConversion: 903 case CK_IntegralToPointer: 904 case CK_PointerToIntegral: 905 case CK_ToVoid: 906 case CK_VectorSplat: 907 case CK_CPointerToObjCPointerCast: 908 case CK_BlockPointerToObjCPointerCast: 909 case CK_AnyPointerToBlockPointerCast: 910 case CK_ObjCObjectLValueCast: 911 case CK_FloatingRealToComplex: 912 case CK_FloatingComplexCast: 913 case CK_FloatingComplexToIntegralComplex: 914 case CK_IntegralRealToComplex: 915 case CK_IntegralComplexCast: 916 case CK_IntegralComplexToFloatingComplex: 917 case CK_ARCProduceObject: 918 case CK_ARCConsumeObject: 919 case CK_ARCReclaimReturnedObject: 920 case CK_ARCExtendBlockObject: 921 case CK_NonAtomicToAtomic: 922 case CK_CopyAndAutoreleaseBlockObject: 923 case CK_BuiltinFnToFnPtr: 924 return false; 925 } 926 } 927 928 if (needsCast) { 929 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 930 // FIXME: Use a custom category name to distinguish migration diagnostics. 931 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 932 "converting to boxing syntax requires casting %0 to %1"); 933 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 934 << Msg->getSourceRange(); 935 return false; 936 } 937 938 SourceRange ArgRange = OrigArg->getSourceRange(); 939 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 940 941 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 942 commit.insertBefore(ArgRange.getBegin(), "@"); 943 else 944 commit.insertWrap("@(", ArgRange, ")"); 945 946 return true; 947} 948 949//===----------------------------------------------------------------------===// 950// rewriteToStringBoxedExpression. 951//===----------------------------------------------------------------------===// 952 953static bool doRewriteToUTF8StringBoxedExpressionHelper( 954 const ObjCMessageExpr *Msg, 955 const NSAPI &NS, Commit &commit) { 956 const Expr *Arg = Msg->getArg(0); 957 if (Arg->isTypeDependent()) 958 return false; 959 960 ASTContext &Ctx = NS.getASTContext(); 961 962 const Expr *OrigArg = Arg->IgnoreImpCasts(); 963 QualType OrigTy = OrigArg->getType(); 964 if (OrigTy->isArrayType()) 965 OrigTy = Ctx.getArrayDecayedType(OrigTy); 966 967 if (const StringLiteral * 968 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 969 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 970 commit.insert(StrE->getLocStart(), "@"); 971 return true; 972 } 973 974 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 975 QualType PointeeType = PT->getPointeeType(); 976 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 977 SourceRange ArgRange = OrigArg->getSourceRange(); 978 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 979 980 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 981 commit.insertBefore(ArgRange.getBegin(), "@"); 982 else 983 commit.insertWrap("@(", ArgRange, ")"); 984 985 return true; 986 } 987 } 988 989 return false; 990} 991 992static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 993 const NSAPI &NS, Commit &commit) { 994 Selector Sel = Msg->getSelector(); 995 996 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 997 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 998 if (Msg->getNumArgs() != 1) 999 return false; 1000 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1001 } 1002 1003 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1004 if (Msg->getNumArgs() != 2) 1005 return false; 1006 1007 const Expr *encodingArg = Msg->getArg(1); 1008 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1009 NS.isNSASCIIStringEncodingConstant(encodingArg)) 1010 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1011 } 1012 1013 return false; 1014} 1015