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 21 using namespace clang; 22 using namespace edit; 23 24 static 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 44 bool 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 80 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 81 Receiver = Receiver->IgnoreImpCasts(); 82 if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) { 83 SourceRange RecRange = Receiver->getSourceRange(); 84 commit.insertWrap("(", RecRange, ")"); 85 } 86 } 87 88 static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) { 89 if (Msg->getNumArgs() != 1) 90 return false; 91 const Expr *Rec = Msg->getInstanceReceiver(); 92 if (!Rec) 93 return false; 94 95 SourceRange MsgRange = Msg->getSourceRange(); 96 SourceRange RecRange = Rec->getSourceRange(); 97 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 98 99 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 100 ArgRange.getBegin()), 101 CharSourceRange::getTokenRange(RecRange)); 102 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 103 ArgRange); 104 commit.insertWrap("[", ArgRange, "]"); 105 maybePutParensOnReceiver(Rec, commit); 106 return true; 107 } 108 109 static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg, 110 Commit &commit) { 111 if (Msg->getNumArgs() != 2) 112 return false; 113 const Expr *Rec = Msg->getInstanceReceiver(); 114 if (!Rec) 115 return false; 116 117 SourceRange MsgRange = Msg->getSourceRange(); 118 SourceRange RecRange = Rec->getSourceRange(); 119 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 120 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 121 122 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 123 Arg0Range.getBegin()), 124 CharSourceRange::getTokenRange(RecRange)); 125 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 126 Arg1Range.getBegin()), 127 CharSourceRange::getTokenRange(Arg0Range)); 128 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 129 Arg1Range); 130 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 131 Arg1Range.getBegin()), 132 "] = "); 133 maybePutParensOnReceiver(Rec, commit); 134 return true; 135 } 136 137 static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg, 138 Commit &commit) { 139 if (Msg->getNumArgs() != 2) 140 return false; 141 const Expr *Rec = Msg->getInstanceReceiver(); 142 if (!Rec) 143 return false; 144 145 SourceRange MsgRange = Msg->getSourceRange(); 146 SourceRange RecRange = Rec->getSourceRange(); 147 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 148 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 149 150 SourceLocation LocBeforeVal = Arg0Range.getBegin(); 151 commit.insertBefore(LocBeforeVal, "] = "); 152 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 153 /*beforePreviousInsertions=*/true); 154 commit.insertBefore(LocBeforeVal, "["); 155 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 156 Arg0Range.getBegin()), 157 CharSourceRange::getTokenRange(RecRange)); 158 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 159 Arg0Range); 160 maybePutParensOnReceiver(Rec, commit); 161 return true; 162 } 163 164 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 165 const NSAPI &NS, Commit &commit) { 166 if (!Msg || Msg->isImplicit() || 167 Msg->getReceiverKind() != ObjCMessageExpr::Instance) 168 return false; 169 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 170 if (!Method) 171 return false; 172 173 const ObjCInterfaceDecl * 174 IFace = NS.getASTContext().getObjContainingInterface( 175 const_cast<ObjCMethodDecl *>(Method)); 176 if (!IFace) 177 return false; 178 IdentifierInfo *II = IFace->getIdentifier(); 179 Selector Sel = Msg->getSelector(); 180 181 if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) && 182 Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) || 183 (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) && 184 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))) 185 return rewriteToSubscriptGet(Msg, commit); 186 187 if (Msg->getNumArgs() != 2) 188 return false; 189 190 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) && 191 Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 192 return rewriteToArraySubscriptSet(Msg, commit); 193 194 if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) && 195 Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 196 return rewriteToDictionarySubscriptSet(Msg, commit); 197 198 return false; 199 } 200 201 //===----------------------------------------------------------------------===// 202 // rewriteToObjCLiteralSyntax. 203 //===----------------------------------------------------------------------===// 204 205 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 206 const NSAPI &NS, Commit &commit); 207 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 208 const NSAPI &NS, Commit &commit); 209 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 210 const NSAPI &NS, Commit &commit); 211 212 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 213 const NSAPI &NS, Commit &commit) { 214 IdentifierInfo *II = 0; 215 if (!checkForLiteralCreation(Msg, II)) 216 return false; 217 218 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 219 return rewriteToArrayLiteral(Msg, NS, commit); 220 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 221 return rewriteToDictionaryLiteral(Msg, NS, commit); 222 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 223 return rewriteToNumberLiteral(Msg, NS, commit); 224 225 return false; 226 } 227 228 //===----------------------------------------------------------------------===// 229 // rewriteToArrayLiteral. 230 //===----------------------------------------------------------------------===// 231 232 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 233 const NSAPI &NS, Commit &commit) { 234 Selector Sel = Msg->getSelector(); 235 SourceRange MsgRange = Msg->getSourceRange(); 236 237 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 238 if (Msg->getNumArgs() != 0) 239 return false; 240 commit.replace(MsgRange, "@[]"); 241 return true; 242 } 243 244 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 245 if (Msg->getNumArgs() != 1) 246 return false; 247 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 248 commit.replaceWithInner(MsgRange, ArgRange); 249 commit.insertWrap("@[", ArgRange, "]"); 250 return true; 251 } 252 253 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) { 254 if (Msg->getNumArgs() == 0) 255 return false; 256 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 257 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 258 return false; 259 260 if (Msg->getNumArgs() == 1) { 261 commit.replace(MsgRange, "@[]"); 262 return true; 263 } 264 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 265 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 266 commit.replaceWithInner(MsgRange, ArgRange); 267 commit.insertWrap("@[", ArgRange, "]"); 268 return true; 269 } 270 271 return false; 272 } 273 274 //===----------------------------------------------------------------------===// 275 // rewriteToDictionaryLiteral. 276 //===----------------------------------------------------------------------===// 277 278 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 279 const NSAPI &NS, Commit &commit) { 280 Selector Sel = Msg->getSelector(); 281 SourceRange MsgRange = Msg->getSourceRange(); 282 283 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 284 if (Msg->getNumArgs() != 0) 285 return false; 286 commit.replace(MsgRange, "@{}"); 287 return true; 288 } 289 290 if (Sel == NS.getNSDictionarySelector( 291 NSAPI::NSDict_dictionaryWithObjectForKey)) { 292 if (Msg->getNumArgs() != 2) 293 return false; 294 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 295 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 296 // Insert key before the value. 297 commit.insertBefore(ValRange.getBegin(), ": "); 298 commit.insertFromRange(ValRange.getBegin(), 299 CharSourceRange::getTokenRange(KeyRange), 300 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 301 commit.insertBefore(ValRange.getBegin(), "@{"); 302 commit.insertAfterToken(ValRange.getEnd(), "}"); 303 commit.replaceWithInner(MsgRange, ValRange); 304 return true; 305 } 306 307 if (Sel == NS.getNSDictionarySelector( 308 NSAPI::NSDict_dictionaryWithObjectsAndKeys)) { 309 if (Msg->getNumArgs() % 2 != 1) 310 return false; 311 unsigned SentinelIdx = Msg->getNumArgs() - 1; 312 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 313 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 314 return false; 315 316 if (Msg->getNumArgs() == 1) { 317 commit.replace(MsgRange, "@{}"); 318 return true; 319 } 320 321 for (unsigned i = 0; i < SentinelIdx; i += 2) { 322 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 323 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 324 // Insert value after key. 325 commit.insertAfterToken(KeyRange.getEnd(), ": "); 326 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 327 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 328 KeyRange.getBegin())); 329 } 330 // Range of arguments up until and including the last key. 331 // The sentinel and first value are cut off, the value will move after the 332 // key. 333 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 334 Msg->getArg(SentinelIdx-1)->getLocEnd()); 335 commit.insertWrap("@{", ArgRange, "}"); 336 commit.replaceWithInner(MsgRange, ArgRange); 337 return true; 338 } 339 340 return false; 341 } 342 343 //===----------------------------------------------------------------------===// 344 // rewriteToNumberLiteral. 345 //===----------------------------------------------------------------------===// 346 347 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 348 const CharacterLiteral *Arg, 349 const NSAPI &NS, Commit &commit) { 350 if (Arg->getKind() != CharacterLiteral::Ascii) 351 return false; 352 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 353 Msg->getSelector())) { 354 SourceRange ArgRange = Arg->getSourceRange(); 355 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 356 commit.insert(ArgRange.getBegin(), "@"); 357 return true; 358 } 359 360 return false; 361 } 362 363 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 364 const Expr *Arg, 365 const NSAPI &NS, Commit &commit) { 366 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 367 Msg->getSelector())) { 368 SourceRange ArgRange = Arg->getSourceRange(); 369 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 370 commit.insert(ArgRange.getBegin(), "@"); 371 return true; 372 } 373 374 return false; 375 } 376 377 namespace { 378 379 struct LiteralInfo { 380 bool Hex, Octal; 381 StringRef U, F, L, LL; 382 CharSourceRange WithoutSuffRange; 383 }; 384 385 } 386 387 static bool getLiteralInfo(SourceRange literalRange, 388 bool isFloat, bool isIntZero, 389 ASTContext &Ctx, LiteralInfo &Info) { 390 if (literalRange.getBegin().isMacroID() || 391 literalRange.getEnd().isMacroID()) 392 return false; 393 StringRef text = Lexer::getSourceText( 394 CharSourceRange::getTokenRange(literalRange), 395 Ctx.getSourceManager(), Ctx.getLangOpts()); 396 if (text.empty()) 397 return false; 398 399 llvm::Optional<bool> UpperU, UpperL; 400 bool UpperF = false; 401 402 struct Suff { 403 static bool has(StringRef suff, StringRef &text) { 404 if (text.endswith(suff)) { 405 text = text.substr(0, text.size()-suff.size()); 406 return true; 407 } 408 return false; 409 } 410 }; 411 412 while (1) { 413 if (Suff::has("u", text)) { 414 UpperU = false; 415 } else if (Suff::has("U", text)) { 416 UpperU = true; 417 } else if (Suff::has("ll", text)) { 418 UpperL = false; 419 } else if (Suff::has("LL", text)) { 420 UpperL = true; 421 } else if (Suff::has("l", text)) { 422 UpperL = false; 423 } else if (Suff::has("L", text)) { 424 UpperL = true; 425 } else if (isFloat && Suff::has("f", text)) { 426 UpperF = false; 427 } else if (isFloat && Suff::has("F", text)) { 428 UpperF = true; 429 } else 430 break; 431 } 432 433 if (!UpperU.hasValue() && !UpperL.hasValue()) 434 UpperU = UpperL = true; 435 else if (UpperU.hasValue() && !UpperL.hasValue()) 436 UpperL = UpperU; 437 else if (UpperL.hasValue() && !UpperU.hasValue()) 438 UpperU = UpperL; 439 440 Info.U = *UpperU ? "U" : "u"; 441 Info.L = *UpperL ? "L" : "l"; 442 Info.LL = *UpperL ? "LL" : "ll"; 443 Info.F = UpperF ? "F" : "f"; 444 445 Info.Hex = Info.Octal = false; 446 if (text.startswith("0x")) 447 Info.Hex = true; 448 else if (!isFloat && !isIntZero && text.startswith("0")) 449 Info.Octal = true; 450 451 SourceLocation B = literalRange.getBegin(); 452 Info.WithoutSuffRange = 453 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 454 return true; 455 } 456 457 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 458 const NSAPI &NS, Commit &commit) { 459 if (Msg->getNumArgs() != 1) 460 return false; 461 462 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 463 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 464 return rewriteToCharLiteral(Msg, CharE, NS, commit); 465 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 466 return rewriteToBoolLiteral(Msg, BE, NS, commit); 467 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 468 return rewriteToBoolLiteral(Msg, BE, NS, commit); 469 470 const Expr *literalE = Arg; 471 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 472 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 473 literalE = UOE->getSubExpr(); 474 } 475 476 // Only integer and floating literals; non-literals or imaginary literal 477 // cannot be rewritten. 478 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 479 return false; 480 481 ASTContext &Ctx = NS.getASTContext(); 482 Selector Sel = Msg->getSelector(); 483 llvm::Optional<NSAPI::NSNumberLiteralMethodKind> 484 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 485 if (!MKOpt) 486 return false; 487 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 488 489 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 490 bool CallIsFloating = false, CallIsDouble = false; 491 492 switch (MK) { 493 // We cannot have these calls with int/float literals. 494 case NSAPI::NSNumberWithChar: 495 case NSAPI::NSNumberWithUnsignedChar: 496 case NSAPI::NSNumberWithShort: 497 case NSAPI::NSNumberWithUnsignedShort: 498 case NSAPI::NSNumberWithBool: 499 return false; 500 501 case NSAPI::NSNumberWithUnsignedInt: 502 case NSAPI::NSNumberWithUnsignedInteger: 503 CallIsUnsigned = true; 504 case NSAPI::NSNumberWithInt: 505 case NSAPI::NSNumberWithInteger: 506 break; 507 508 case NSAPI::NSNumberWithUnsignedLong: 509 CallIsUnsigned = true; 510 case NSAPI::NSNumberWithLong: 511 CallIsLong = true; 512 break; 513 514 case NSAPI::NSNumberWithUnsignedLongLong: 515 CallIsUnsigned = true; 516 case NSAPI::NSNumberWithLongLong: 517 CallIsLongLong = true; 518 break; 519 520 case NSAPI::NSNumberWithDouble: 521 CallIsDouble = true; 522 case NSAPI::NSNumberWithFloat: 523 CallIsFloating = true; 524 break; 525 } 526 527 SourceRange ArgRange = Arg->getSourceRange(); 528 QualType ArgTy = Arg->getType(); 529 QualType CallTy = Msg->getArg(0)->getType(); 530 531 // Check for the easy case, the literal maps directly to the call. 532 if (Ctx.hasSameType(ArgTy, CallTy)) { 533 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 534 commit.insert(ArgRange.getBegin(), "@"); 535 return true; 536 } 537 538 // We will need to modify the literal suffix to get the same type as the call. 539 // Don't even try if it came from a macro. 540 if (ArgRange.getBegin().isMacroID()) 541 return false; 542 543 bool LitIsFloat = ArgTy->isFloatingType(); 544 // For a float passed to integer call, don't try rewriting. It is difficult 545 // and a very uncommon case anyway. 546 if (LitIsFloat && !CallIsFloating) 547 return false; 548 549 // Try to modify the literal make it the same type as the method call. 550 // -Modify the suffix, and/or 551 // -Change integer to float 552 553 LiteralInfo LitInfo; 554 bool isIntZero = false; 555 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 556 isIntZero = !IntE->getValue().getBoolValue(); 557 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 558 return false; 559 560 // Not easy to do int -> float with hex/octal and uncommon anyway. 561 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 562 return false; 563 564 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 565 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 566 567 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 568 LitInfo.WithoutSuffRange); 569 commit.insert(LitB, "@"); 570 571 if (!LitIsFloat && CallIsFloating) 572 commit.insert(LitE, ".0"); 573 574 if (CallIsFloating) { 575 if (!CallIsDouble) 576 commit.insert(LitE, LitInfo.F); 577 } else { 578 if (CallIsUnsigned) 579 commit.insert(LitE, LitInfo.U); 580 581 if (CallIsLong) 582 commit.insert(LitE, LitInfo.L); 583 else if (CallIsLongLong) 584 commit.insert(LitE, LitInfo.LL); 585 } 586 return true; 587 } 588