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