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