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