RewriteObjCFoundationAPI.cpp revision c5d33e959b45987bb97a7fcf639cdf5ce55987ed
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<ParenExpr>(FullExpr) || 628 isa<ParenListExpr>(Expr) || 629 isa<SizeOfPackExpr>(Expr)) 630 return false; 631 632 return true; 633} 634static bool castOperatorNeedsParens(const Expr *FullExpr) { 635 const Expr* Expr = FullExpr->IgnoreImpCasts(); 636 if (isa<ArraySubscriptExpr>(Expr) || 637 isa<CallExpr>(Expr) || 638 isa<DeclRefExpr>(Expr) || 639 isa<CastExpr>(Expr) || 640 isa<CXXNewExpr>(Expr) || 641 isa<CXXConstructExpr>(Expr) || 642 isa<CXXDeleteExpr>(Expr) || 643 isa<CXXNoexceptExpr>(Expr) || 644 isa<CXXPseudoDestructorExpr>(Expr) || 645 isa<CXXScalarValueInitExpr>(Expr) || 646 isa<CXXThisExpr>(Expr) || 647 isa<CXXTypeidExpr>(Expr) || 648 isa<CXXUnresolvedConstructExpr>(Expr) || 649 isa<ObjCMessageExpr>(Expr) || 650 isa<ObjCPropertyRefExpr>(Expr) || 651 isa<ObjCProtocolExpr>(Expr) || 652 isa<MemberExpr>(Expr) || 653 isa<ParenExpr>(FullExpr) || 654 isa<ParenListExpr>(Expr) || 655 isa<SizeOfPackExpr>(Expr) || 656 isa<UnaryOperator>(Expr)) 657 return false; 658 659 return true; 660} 661 662static void objectifyExpr(const Expr *E, Commit &commit) { 663 if (!E) return; 664 665 QualType T = E->getType(); 666 if (T->isObjCObjectPointerType()) { 667 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 668 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 669 return; 670 } else { 671 return; 672 } 673 } else if (!T->isPointerType()) { 674 return; 675 } 676 677 SourceRange Range = E->getSourceRange(); 678 if (castOperatorNeedsParens(E)) 679 commit.insertWrap("(", Range, ")"); 680 commit.insertBefore(Range.getBegin(), "(id)"); 681} 682 683//===----------------------------------------------------------------------===// 684// rewriteToNumericBoxedExpression. 685//===----------------------------------------------------------------------===// 686 687static bool isEnumConstant(const Expr *E) { 688 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 689 if (const ValueDecl *VD = DRE->getDecl()) 690 return isa<EnumConstantDecl>(VD); 691 692 return false; 693} 694 695static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 696 const NSAPI &NS, Commit &commit) { 697 if (Msg->getNumArgs() != 1) 698 return false; 699 700 const Expr *Arg = Msg->getArg(0); 701 if (Arg->isTypeDependent()) 702 return false; 703 704 ASTContext &Ctx = NS.getASTContext(); 705 Selector Sel = Msg->getSelector(); 706 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 707 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 708 if (!MKOpt) 709 return false; 710 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 711 712 const Expr *OrigArg = Arg->IgnoreImpCasts(); 713 QualType FinalTy = Arg->getType(); 714 QualType OrigTy = OrigArg->getType(); 715 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 716 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 717 718 bool isTruncated = FinalTySize < OrigTySize; 719 bool needsCast = false; 720 721 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 722 switch (ICE->getCastKind()) { 723 case CK_LValueToRValue: 724 case CK_NoOp: 725 case CK_UserDefinedConversion: 726 break; 727 728 case CK_IntegralCast: { 729 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 730 break; 731 // Be more liberal with Integer/UnsignedInteger which are very commonly 732 // used. 733 if ((MK == NSAPI::NSNumberWithInteger || 734 MK == NSAPI::NSNumberWithUnsignedInteger) && 735 !isTruncated) { 736 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 737 break; 738 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 739 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 740 break; 741 } 742 743 needsCast = true; 744 break; 745 } 746 747 case CK_PointerToBoolean: 748 case CK_IntegralToBoolean: 749 case CK_IntegralToFloating: 750 case CK_FloatingToIntegral: 751 case CK_FloatingToBoolean: 752 case CK_FloatingCast: 753 case CK_FloatingComplexToReal: 754 case CK_FloatingComplexToBoolean: 755 case CK_IntegralComplexToReal: 756 case CK_IntegralComplexToBoolean: 757 case CK_AtomicToNonAtomic: 758 needsCast = true; 759 break; 760 761 case CK_Dependent: 762 case CK_BitCast: 763 case CK_LValueBitCast: 764 case CK_BaseToDerived: 765 case CK_DerivedToBase: 766 case CK_UncheckedDerivedToBase: 767 case CK_Dynamic: 768 case CK_ToUnion: 769 case CK_ArrayToPointerDecay: 770 case CK_FunctionToPointerDecay: 771 case CK_NullToPointer: 772 case CK_NullToMemberPointer: 773 case CK_BaseToDerivedMemberPointer: 774 case CK_DerivedToBaseMemberPointer: 775 case CK_MemberPointerToBoolean: 776 case CK_ReinterpretMemberPointer: 777 case CK_ConstructorConversion: 778 case CK_IntegralToPointer: 779 case CK_PointerToIntegral: 780 case CK_ToVoid: 781 case CK_VectorSplat: 782 case CK_CPointerToObjCPointerCast: 783 case CK_BlockPointerToObjCPointerCast: 784 case CK_AnyPointerToBlockPointerCast: 785 case CK_ObjCObjectLValueCast: 786 case CK_FloatingRealToComplex: 787 case CK_FloatingComplexCast: 788 case CK_FloatingComplexToIntegralComplex: 789 case CK_IntegralRealToComplex: 790 case CK_IntegralComplexCast: 791 case CK_IntegralComplexToFloatingComplex: 792 case CK_ARCProduceObject: 793 case CK_ARCConsumeObject: 794 case CK_ARCReclaimReturnedObject: 795 case CK_ARCExtendBlockObject: 796 case CK_NonAtomicToAtomic: 797 case CK_CopyAndAutoreleaseBlockObject: 798 return false; 799 } 800 } 801 802 if (needsCast) 803 return false; 804 805 SourceRange ArgRange = OrigArg->getSourceRange(); 806 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 807 808 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 809 commit.insertBefore(ArgRange.getBegin(), "@"); 810 else 811 commit.insertWrap("@(", ArgRange, ")"); 812 813 return true; 814} 815 816//===----------------------------------------------------------------------===// 817// rewriteToStringBoxedExpression. 818//===----------------------------------------------------------------------===// 819 820static bool doRewriteToUTF8StringBoxedExpressionHelper( 821 const ObjCMessageExpr *Msg, 822 const NSAPI &NS, Commit &commit) { 823 const Expr *Arg = Msg->getArg(0); 824 if (Arg->isTypeDependent()) 825 return false; 826 827 const Expr *OrigArg = Arg->IgnoreImpCasts(); 828 QualType OrigTy = OrigArg->getType(); 829 830 if (const StringLiteral * 831 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 832 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 833 commit.insert(StrE->getLocStart(), "@"); 834 return true; 835 } 836 837 ASTContext &Ctx = NS.getASTContext(); 838 839 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 840 QualType PointeeType = PT->getPointeeType(); 841 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 842 SourceRange ArgRange = OrigArg->getSourceRange(); 843 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 844 845 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 846 commit.insertBefore(ArgRange.getBegin(), "@"); 847 else 848 commit.insertWrap("@(", ArgRange, ")"); 849 850 return true; 851 } 852 } 853 854 return false; 855} 856 857static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 858 const NSAPI &NS, Commit &commit) { 859 Selector Sel = Msg->getSelector(); 860 861 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 862 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 863 if (Msg->getNumArgs() != 1) 864 return false; 865 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 866 } 867 868 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 869 if (Msg->getNumArgs() != 2) 870 return false; 871 872 const Expr *encodingArg = Msg->getArg(1); 873 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 874 NS.isNSASCIIStringEncodingConstant(encodingArg)) 875 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 876 } 877 878 return false; 879} 880