RewriteObjCFoundationAPI.cpp revision 478851c3ed6bd784e7377dffd8e57b200c1b9ba9
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 97static bool subscriptOperatorNeedsParens(const Expr *FullExpr); 98 99static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 100 if (subscriptOperatorNeedsParens(Receiver)) { 101 SourceRange RecRange = Receiver->getSourceRange(); 102 commit.insertWrap("(", RecRange, ")"); 103 } 104} 105 106static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 107 Commit &commit) { 108 if (Msg->getNumArgs() != 1) 109 return false; 110 const Expr *Rec = Msg->getInstanceReceiver(); 111 if (!Rec) 112 return false; 113 114 SourceRange MsgRange = Msg->getSourceRange(); 115 SourceRange RecRange = Rec->getSourceRange(); 116 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 117 118 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 119 ArgRange.getBegin()), 120 CharSourceRange::getTokenRange(RecRange)); 121 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 122 ArgRange); 123 commit.insertWrap("[", ArgRange, "]"); 124 maybePutParensOnReceiver(Rec, commit); 125 return true; 126} 127 128static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 129 const ObjCMessageExpr *Msg, 130 const NSAPI &NS, 131 Commit &commit) { 132 if (!IFace->lookupInstanceMethod(NS.getObjectAtIndexedSubscriptSelector())) 133 return false; 134 return rewriteToSubscriptGetCommon(Msg, commit); 135} 136 137static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 138 const ObjCMessageExpr *Msg, 139 const NSAPI &NS, 140 Commit &commit) { 141 if (!IFace->lookupInstanceMethod(NS.getObjectForKeyedSubscriptSelector())) 142 return false; 143 return rewriteToSubscriptGetCommon(Msg, commit); 144} 145 146static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 147 const ObjCMessageExpr *Msg, 148 const NSAPI &NS, 149 Commit &commit) { 150 if (Msg->getNumArgs() != 2) 151 return false; 152 const Expr *Rec = Msg->getInstanceReceiver(); 153 if (!Rec) 154 return false; 155 if (!IFace->lookupInstanceMethod(NS.getSetObjectAtIndexedSubscriptSelector())) 156 return false; 157 158 SourceRange MsgRange = Msg->getSourceRange(); 159 SourceRange RecRange = Rec->getSourceRange(); 160 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 161 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 162 163 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 164 Arg0Range.getBegin()), 165 CharSourceRange::getTokenRange(RecRange)); 166 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 167 Arg1Range.getBegin()), 168 CharSourceRange::getTokenRange(Arg0Range)); 169 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 170 Arg1Range); 171 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 172 Arg1Range.getBegin()), 173 "] = "); 174 maybePutParensOnReceiver(Rec, commit); 175 return true; 176} 177 178static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 179 const ObjCMessageExpr *Msg, 180 const NSAPI &NS, 181 Commit &commit) { 182 if (Msg->getNumArgs() != 2) 183 return false; 184 const Expr *Rec = Msg->getInstanceReceiver(); 185 if (!Rec) 186 return false; 187 if (!IFace->lookupInstanceMethod(NS.getSetObjectForKeyedSubscriptSelector())) 188 return false; 189 190 SourceRange MsgRange = Msg->getSourceRange(); 191 SourceRange RecRange = Rec->getSourceRange(); 192 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 193 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 194 195 SourceLocation LocBeforeVal = Arg0Range.getBegin(); 196 commit.insertBefore(LocBeforeVal, "] = "); 197 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 198 /*beforePreviousInsertions=*/true); 199 commit.insertBefore(LocBeforeVal, "["); 200 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 201 Arg0Range.getBegin()), 202 CharSourceRange::getTokenRange(RecRange)); 203 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 204 Arg0Range); 205 maybePutParensOnReceiver(Rec, commit); 206 return true; 207} 208 209bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 210 const NSAPI &NS, Commit &commit) { 211 if (!Msg || Msg->isImplicit() || 212 Msg->getReceiverKind() != ObjCMessageExpr::Instance) 213 return false; 214 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 215 if (!Method) 216 return false; 217 218 const ObjCInterfaceDecl * 219 IFace = NS.getASTContext().getObjContainingInterface( 220 const_cast<ObjCMethodDecl *>(Method)); 221 if (!IFace) 222 return false; 223 IdentifierInfo *II = IFace->getIdentifier(); 224 Selector Sel = Msg->getSelector(); 225 226 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray) && 227 Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 228 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 229 230 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) && 231 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 232 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 233 234 if (Msg->getNumArgs() != 2) 235 return false; 236 237 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) && 238 Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 239 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 240 241 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) && 242 Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 243 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 244 245 return false; 246} 247 248//===----------------------------------------------------------------------===// 249// rewriteToObjCLiteralSyntax. 250//===----------------------------------------------------------------------===// 251 252static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 253 const NSAPI &NS, Commit &commit); 254static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 255 const NSAPI &NS, Commit &commit); 256static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 257 const NSAPI &NS, Commit &commit); 258static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 259 const NSAPI &NS, Commit &commit); 260static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 261 const NSAPI &NS, Commit &commit); 262 263bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 264 const NSAPI &NS, Commit &commit) { 265 IdentifierInfo *II = 0; 266 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 267 return false; 268 269 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 270 return rewriteToArrayLiteral(Msg, NS, commit); 271 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 272 return rewriteToDictionaryLiteral(Msg, NS, commit); 273 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 274 return rewriteToNumberLiteral(Msg, NS, commit); 275 if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 276 return rewriteToStringBoxedExpression(Msg, NS, commit); 277 278 return false; 279} 280 281//===----------------------------------------------------------------------===// 282// rewriteToArrayLiteral. 283//===----------------------------------------------------------------------===// 284 285/// \brief Adds an explicit cast to 'id' if the type is not objc object. 286static void objectifyExpr(const Expr *E, Commit &commit); 287 288static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 289 const NSAPI &NS, Commit &commit) { 290 Selector Sel = Msg->getSelector(); 291 SourceRange MsgRange = Msg->getSourceRange(); 292 293 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 294 if (Msg->getNumArgs() != 0) 295 return false; 296 commit.replace(MsgRange, "@[]"); 297 return true; 298 } 299 300 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 301 if (Msg->getNumArgs() != 1) 302 return false; 303 objectifyExpr(Msg->getArg(0), commit); 304 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 305 commit.replaceWithInner(MsgRange, ArgRange); 306 commit.insertWrap("@[", ArgRange, "]"); 307 return true; 308 } 309 310 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 311 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 312 if (Msg->getNumArgs() == 0) 313 return false; 314 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 315 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 316 return false; 317 318 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 319 objectifyExpr(Msg->getArg(i), commit); 320 321 if (Msg->getNumArgs() == 1) { 322 commit.replace(MsgRange, "@[]"); 323 return true; 324 } 325 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 326 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 327 commit.replaceWithInner(MsgRange, ArgRange); 328 commit.insertWrap("@[", ArgRange, "]"); 329 return true; 330 } 331 332 return false; 333} 334 335//===----------------------------------------------------------------------===// 336// rewriteToDictionaryLiteral. 337//===----------------------------------------------------------------------===// 338 339static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 340 const NSAPI &NS, Commit &commit) { 341 Selector Sel = Msg->getSelector(); 342 SourceRange MsgRange = Msg->getSourceRange(); 343 344 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 345 if (Msg->getNumArgs() != 0) 346 return false; 347 commit.replace(MsgRange, "@{}"); 348 return true; 349 } 350 351 if (Sel == NS.getNSDictionarySelector( 352 NSAPI::NSDict_dictionaryWithObjectForKey)) { 353 if (Msg->getNumArgs() != 2) 354 return false; 355 356 objectifyExpr(Msg->getArg(0), commit); 357 objectifyExpr(Msg->getArg(1), commit); 358 359 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 360 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 361 // Insert key before the value. 362 commit.insertBefore(ValRange.getBegin(), ": "); 363 commit.insertFromRange(ValRange.getBegin(), 364 CharSourceRange::getTokenRange(KeyRange), 365 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 366 commit.insertBefore(ValRange.getBegin(), "@{"); 367 commit.insertAfterToken(ValRange.getEnd(), "}"); 368 commit.replaceWithInner(MsgRange, ValRange); 369 return true; 370 } 371 372 if (Sel == NS.getNSDictionarySelector( 373 NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 374 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 375 if (Msg->getNumArgs() % 2 != 1) 376 return false; 377 unsigned SentinelIdx = Msg->getNumArgs() - 1; 378 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 379 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 380 return false; 381 382 if (Msg->getNumArgs() == 1) { 383 commit.replace(MsgRange, "@{}"); 384 return true; 385 } 386 387 for (unsigned i = 0; i < SentinelIdx; i += 2) { 388 objectifyExpr(Msg->getArg(i), commit); 389 objectifyExpr(Msg->getArg(i+1), commit); 390 391 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 392 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 393 // Insert value after key. 394 commit.insertAfterToken(KeyRange.getEnd(), ": "); 395 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 396 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 397 KeyRange.getBegin())); 398 } 399 // Range of arguments up until and including the last key. 400 // The sentinel and first value are cut off, the value will move after the 401 // key. 402 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 403 Msg->getArg(SentinelIdx-1)->getLocEnd()); 404 commit.insertWrap("@{", ArgRange, "}"); 405 commit.replaceWithInner(MsgRange, ArgRange); 406 return true; 407 } 408 409 return false; 410} 411 412//===----------------------------------------------------------------------===// 413// rewriteToNumberLiteral. 414//===----------------------------------------------------------------------===// 415 416static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 417 const CharacterLiteral *Arg, 418 const NSAPI &NS, Commit &commit) { 419 if (Arg->getKind() != CharacterLiteral::Ascii) 420 return false; 421 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 422 Msg->getSelector())) { 423 SourceRange ArgRange = Arg->getSourceRange(); 424 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 425 commit.insert(ArgRange.getBegin(), "@"); 426 return true; 427 } 428 429 return rewriteToNumericBoxedExpression(Msg, NS, commit); 430} 431 432static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 433 const Expr *Arg, 434 const NSAPI &NS, Commit &commit) { 435 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 436 Msg->getSelector())) { 437 SourceRange ArgRange = Arg->getSourceRange(); 438 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 439 commit.insert(ArgRange.getBegin(), "@"); 440 return true; 441 } 442 443 return rewriteToNumericBoxedExpression(Msg, NS, commit); 444} 445 446namespace { 447 448struct LiteralInfo { 449 bool Hex, Octal; 450 StringRef U, F, L, LL; 451 CharSourceRange WithoutSuffRange; 452}; 453 454} 455 456static bool getLiteralInfo(SourceRange literalRange, 457 bool isFloat, bool isIntZero, 458 ASTContext &Ctx, LiteralInfo &Info) { 459 if (literalRange.getBegin().isMacroID() || 460 literalRange.getEnd().isMacroID()) 461 return false; 462 StringRef text = Lexer::getSourceText( 463 CharSourceRange::getTokenRange(literalRange), 464 Ctx.getSourceManager(), Ctx.getLangOpts()); 465 if (text.empty()) 466 return false; 467 468 llvm::Optional<bool> UpperU, UpperL; 469 bool UpperF = false; 470 471 struct Suff { 472 static bool has(StringRef suff, StringRef &text) { 473 if (text.endswith(suff)) { 474 text = text.substr(0, text.size()-suff.size()); 475 return true; 476 } 477 return false; 478 } 479 }; 480 481 while (1) { 482 if (Suff::has("u", text)) { 483 UpperU = false; 484 } else if (Suff::has("U", text)) { 485 UpperU = true; 486 } else if (Suff::has("ll", text)) { 487 UpperL = false; 488 } else if (Suff::has("LL", text)) { 489 UpperL = true; 490 } else if (Suff::has("l", text)) { 491 UpperL = false; 492 } else if (Suff::has("L", text)) { 493 UpperL = true; 494 } else if (isFloat && Suff::has("f", text)) { 495 UpperF = false; 496 } else if (isFloat && Suff::has("F", text)) { 497 UpperF = true; 498 } else 499 break; 500 } 501 502 if (!UpperU.hasValue() && !UpperL.hasValue()) 503 UpperU = UpperL = true; 504 else if (UpperU.hasValue() && !UpperL.hasValue()) 505 UpperL = UpperU; 506 else if (UpperL.hasValue() && !UpperU.hasValue()) 507 UpperU = UpperL; 508 509 Info.U = *UpperU ? "U" : "u"; 510 Info.L = *UpperL ? "L" : "l"; 511 Info.LL = *UpperL ? "LL" : "ll"; 512 Info.F = UpperF ? "F" : "f"; 513 514 Info.Hex = Info.Octal = false; 515 if (text.startswith("0x")) 516 Info.Hex = true; 517 else if (!isFloat && !isIntZero && text.startswith("0")) 518 Info.Octal = true; 519 520 SourceLocation B = literalRange.getBegin(); 521 Info.WithoutSuffRange = 522 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 523 return true; 524} 525 526static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 527 const NSAPI &NS, Commit &commit) { 528 if (Msg->getNumArgs() != 1) 529 return false; 530 531 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 532 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 533 return rewriteToCharLiteral(Msg, CharE, NS, commit); 534 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 535 return rewriteToBoolLiteral(Msg, BE, NS, commit); 536 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 537 return rewriteToBoolLiteral(Msg, BE, NS, commit); 538 539 const Expr *literalE = Arg; 540 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 541 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 542 literalE = UOE->getSubExpr(); 543 } 544 545 // Only integer and floating literals, otherwise try to rewrite to boxed 546 // expression. 547 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 548 return rewriteToNumericBoxedExpression(Msg, NS, commit); 549 550 ASTContext &Ctx = NS.getASTContext(); 551 Selector Sel = Msg->getSelector(); 552 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 553 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 554 if (!MKOpt) 555 return false; 556 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 557 558 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 559 bool CallIsFloating = false, CallIsDouble = false; 560 561 switch (MK) { 562 // We cannot have these calls with int/float literals. 563 case NSAPI::NSNumberWithChar: 564 case NSAPI::NSNumberWithUnsignedChar: 565 case NSAPI::NSNumberWithShort: 566 case NSAPI::NSNumberWithUnsignedShort: 567 case NSAPI::NSNumberWithBool: 568 return rewriteToNumericBoxedExpression(Msg, NS, commit); 569 570 case NSAPI::NSNumberWithUnsignedInt: 571 case NSAPI::NSNumberWithUnsignedInteger: 572 CallIsUnsigned = true; 573 case NSAPI::NSNumberWithInt: 574 case NSAPI::NSNumberWithInteger: 575 break; 576 577 case NSAPI::NSNumberWithUnsignedLong: 578 CallIsUnsigned = true; 579 case NSAPI::NSNumberWithLong: 580 CallIsLong = true; 581 break; 582 583 case NSAPI::NSNumberWithUnsignedLongLong: 584 CallIsUnsigned = true; 585 case NSAPI::NSNumberWithLongLong: 586 CallIsLongLong = true; 587 break; 588 589 case NSAPI::NSNumberWithDouble: 590 CallIsDouble = true; 591 case NSAPI::NSNumberWithFloat: 592 CallIsFloating = true; 593 break; 594 } 595 596 SourceRange ArgRange = Arg->getSourceRange(); 597 QualType ArgTy = Arg->getType(); 598 QualType CallTy = Msg->getArg(0)->getType(); 599 600 // Check for the easy case, the literal maps directly to the call. 601 if (Ctx.hasSameType(ArgTy, CallTy)) { 602 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 603 commit.insert(ArgRange.getBegin(), "@"); 604 return true; 605 } 606 607 // We will need to modify the literal suffix to get the same type as the call. 608 // Try with boxed expression if it came from a macro. 609 if (ArgRange.getBegin().isMacroID()) 610 return rewriteToNumericBoxedExpression(Msg, NS, commit); 611 612 bool LitIsFloat = ArgTy->isFloatingType(); 613 // For a float passed to integer call, don't try rewriting to objc literal. 614 // It is difficult and a very uncommon case anyway. 615 // But try with boxed expression. 616 if (LitIsFloat && !CallIsFloating) 617 return rewriteToNumericBoxedExpression(Msg, NS, commit); 618 619 // Try to modify the literal make it the same type as the method call. 620 // -Modify the suffix, and/or 621 // -Change integer to float 622 623 LiteralInfo LitInfo; 624 bool isIntZero = false; 625 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 626 isIntZero = !IntE->getValue().getBoolValue(); 627 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 628 return rewriteToNumericBoxedExpression(Msg, NS, commit); 629 630 // Not easy to do int -> float with hex/octal and uncommon anyway. 631 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 632 return rewriteToNumericBoxedExpression(Msg, NS, commit); 633 634 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 635 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 636 637 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 638 LitInfo.WithoutSuffRange); 639 commit.insert(LitB, "@"); 640 641 if (!LitIsFloat && CallIsFloating) 642 commit.insert(LitE, ".0"); 643 644 if (CallIsFloating) { 645 if (!CallIsDouble) 646 commit.insert(LitE, LitInfo.F); 647 } else { 648 if (CallIsUnsigned) 649 commit.insert(LitE, LitInfo.U); 650 651 if (CallIsLong) 652 commit.insert(LitE, LitInfo.L); 653 else if (CallIsLongLong) 654 commit.insert(LitE, LitInfo.LL); 655 } 656 return true; 657} 658 659// FIXME: Make determination of operator precedence more general and 660// make it broadly available. 661static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 662 const Expr* Expr = FullExpr->IgnoreImpCasts(); 663 if (isa<ArraySubscriptExpr>(Expr) || 664 isa<CallExpr>(Expr) || 665 isa<DeclRefExpr>(Expr) || 666 isa<CXXNamedCastExpr>(Expr) || 667 isa<CXXConstructExpr>(Expr) || 668 isa<CXXThisExpr>(Expr) || 669 isa<CXXTypeidExpr>(Expr) || 670 isa<CXXUnresolvedConstructExpr>(Expr) || 671 isa<ObjCMessageExpr>(Expr) || 672 isa<ObjCPropertyRefExpr>(Expr) || 673 isa<ObjCProtocolExpr>(Expr) || 674 isa<MemberExpr>(Expr) || 675 isa<ObjCIvarRefExpr>(Expr) || 676 isa<ParenExpr>(FullExpr) || 677 isa<ParenListExpr>(Expr) || 678 isa<SizeOfPackExpr>(Expr)) 679 return false; 680 681 return true; 682} 683static bool castOperatorNeedsParens(const Expr *FullExpr) { 684 const Expr* Expr = FullExpr->IgnoreImpCasts(); 685 if (isa<ArraySubscriptExpr>(Expr) || 686 isa<CallExpr>(Expr) || 687 isa<DeclRefExpr>(Expr) || 688 isa<CastExpr>(Expr) || 689 isa<CXXNewExpr>(Expr) || 690 isa<CXXConstructExpr>(Expr) || 691 isa<CXXDeleteExpr>(Expr) || 692 isa<CXXNoexceptExpr>(Expr) || 693 isa<CXXPseudoDestructorExpr>(Expr) || 694 isa<CXXScalarValueInitExpr>(Expr) || 695 isa<CXXThisExpr>(Expr) || 696 isa<CXXTypeidExpr>(Expr) || 697 isa<CXXUnresolvedConstructExpr>(Expr) || 698 isa<ObjCMessageExpr>(Expr) || 699 isa<ObjCPropertyRefExpr>(Expr) || 700 isa<ObjCProtocolExpr>(Expr) || 701 isa<MemberExpr>(Expr) || 702 isa<ObjCIvarRefExpr>(Expr) || 703 isa<ParenExpr>(FullExpr) || 704 isa<ParenListExpr>(Expr) || 705 isa<SizeOfPackExpr>(Expr) || 706 isa<UnaryOperator>(Expr)) 707 return false; 708 709 return true; 710} 711 712static void objectifyExpr(const Expr *E, Commit &commit) { 713 if (!E) return; 714 715 QualType T = E->getType(); 716 if (T->isObjCObjectPointerType()) { 717 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 718 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 719 return; 720 } else { 721 return; 722 } 723 } else if (!T->isPointerType()) { 724 return; 725 } 726 727 SourceRange Range = E->getSourceRange(); 728 if (castOperatorNeedsParens(E)) 729 commit.insertWrap("(", Range, ")"); 730 commit.insertBefore(Range.getBegin(), "(id)"); 731} 732 733//===----------------------------------------------------------------------===// 734// rewriteToNumericBoxedExpression. 735//===----------------------------------------------------------------------===// 736 737static bool isEnumConstant(const Expr *E) { 738 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 739 if (const ValueDecl *VD = DRE->getDecl()) 740 return isa<EnumConstantDecl>(VD); 741 742 return false; 743} 744 745static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 746 const NSAPI &NS, Commit &commit) { 747 if (Msg->getNumArgs() != 1) 748 return false; 749 750 const Expr *Arg = Msg->getArg(0); 751 if (Arg->isTypeDependent()) 752 return false; 753 754 ASTContext &Ctx = NS.getASTContext(); 755 Selector Sel = Msg->getSelector(); 756 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 757 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 758 if (!MKOpt) 759 return false; 760 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 761 762 const Expr *OrigArg = Arg->IgnoreImpCasts(); 763 QualType FinalTy = Arg->getType(); 764 QualType OrigTy = OrigArg->getType(); 765 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 766 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 767 768 bool isTruncated = FinalTySize < OrigTySize; 769 bool needsCast = false; 770 771 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 772 switch (ICE->getCastKind()) { 773 case CK_LValueToRValue: 774 case CK_NoOp: 775 case CK_UserDefinedConversion: 776 break; 777 778 case CK_IntegralCast: { 779 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 780 break; 781 // Be more liberal with Integer/UnsignedInteger which are very commonly 782 // used. 783 if ((MK == NSAPI::NSNumberWithInteger || 784 MK == NSAPI::NSNumberWithUnsignedInteger) && 785 !isTruncated) { 786 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 787 break; 788 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 789 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 790 break; 791 } 792 793 needsCast = true; 794 break; 795 } 796 797 case CK_PointerToBoolean: 798 case CK_IntegralToBoolean: 799 case CK_IntegralToFloating: 800 case CK_FloatingToIntegral: 801 case CK_FloatingToBoolean: 802 case CK_FloatingCast: 803 case CK_FloatingComplexToReal: 804 case CK_FloatingComplexToBoolean: 805 case CK_IntegralComplexToReal: 806 case CK_IntegralComplexToBoolean: 807 case CK_AtomicToNonAtomic: 808 needsCast = true; 809 break; 810 811 case CK_Dependent: 812 case CK_BitCast: 813 case CK_LValueBitCast: 814 case CK_BaseToDerived: 815 case CK_DerivedToBase: 816 case CK_UncheckedDerivedToBase: 817 case CK_Dynamic: 818 case CK_ToUnion: 819 case CK_ArrayToPointerDecay: 820 case CK_FunctionToPointerDecay: 821 case CK_NullToPointer: 822 case CK_NullToMemberPointer: 823 case CK_BaseToDerivedMemberPointer: 824 case CK_DerivedToBaseMemberPointer: 825 case CK_MemberPointerToBoolean: 826 case CK_ReinterpretMemberPointer: 827 case CK_ConstructorConversion: 828 case CK_IntegralToPointer: 829 case CK_PointerToIntegral: 830 case CK_ToVoid: 831 case CK_VectorSplat: 832 case CK_CPointerToObjCPointerCast: 833 case CK_BlockPointerToObjCPointerCast: 834 case CK_AnyPointerToBlockPointerCast: 835 case CK_ObjCObjectLValueCast: 836 case CK_FloatingRealToComplex: 837 case CK_FloatingComplexCast: 838 case CK_FloatingComplexToIntegralComplex: 839 case CK_IntegralRealToComplex: 840 case CK_IntegralComplexCast: 841 case CK_IntegralComplexToFloatingComplex: 842 case CK_ARCProduceObject: 843 case CK_ARCConsumeObject: 844 case CK_ARCReclaimReturnedObject: 845 case CK_ARCExtendBlockObject: 846 case CK_NonAtomicToAtomic: 847 case CK_CopyAndAutoreleaseBlockObject: 848 return false; 849 } 850 } 851 852 if (needsCast) { 853 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 854 // FIXME: Use a custom category name to distinguish migration diagnostics. 855 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 856 "converting to boxing syntax requires casting %0 to %1"); 857 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 858 << Msg->getSourceRange(); 859 return false; 860 } 861 862 SourceRange ArgRange = OrigArg->getSourceRange(); 863 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 864 865 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 866 commit.insertBefore(ArgRange.getBegin(), "@"); 867 else 868 commit.insertWrap("@(", ArgRange, ")"); 869 870 return true; 871} 872 873//===----------------------------------------------------------------------===// 874// rewriteToStringBoxedExpression. 875//===----------------------------------------------------------------------===// 876 877static bool doRewriteToUTF8StringBoxedExpressionHelper( 878 const ObjCMessageExpr *Msg, 879 const NSAPI &NS, Commit &commit) { 880 const Expr *Arg = Msg->getArg(0); 881 if (Arg->isTypeDependent()) 882 return false; 883 884 ASTContext &Ctx = NS.getASTContext(); 885 886 const Expr *OrigArg = Arg->IgnoreImpCasts(); 887 QualType OrigTy = OrigArg->getType(); 888 if (OrigTy->isArrayType()) 889 OrigTy = Ctx.getArrayDecayedType(OrigTy); 890 891 if (const StringLiteral * 892 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 893 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 894 commit.insert(StrE->getLocStart(), "@"); 895 return true; 896 } 897 898 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 899 QualType PointeeType = PT->getPointeeType(); 900 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 901 SourceRange ArgRange = OrigArg->getSourceRange(); 902 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 903 904 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 905 commit.insertBefore(ArgRange.getBegin(), "@"); 906 else 907 commit.insertWrap("@(", ArgRange, ")"); 908 909 return true; 910 } 911 } 912 913 return false; 914} 915 916static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 917 const NSAPI &NS, Commit &commit) { 918 Selector Sel = Msg->getSelector(); 919 920 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 921 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 922 if (Msg->getNumArgs() != 1) 923 return false; 924 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 925 } 926 927 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 928 if (Msg->getNumArgs() != 2) 929 return false; 930 931 const Expr *encodingArg = Msg->getArg(1); 932 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 933 NS.isNSASCIIStringEncodingConstant(encodingArg)) 934 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 935 } 936 937 return false; 938} 939