clang API Documentation
00001 //===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // Rewrites legacy method calls to modern syntax. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "clang/Edit/Rewriters.h" 00015 #include "clang/AST/ASTContext.h" 00016 #include "clang/AST/ExprCXX.h" 00017 #include "clang/AST/ExprObjC.h" 00018 #include "clang/AST/NSAPI.h" 00019 #include "clang/AST/ParentMap.h" 00020 #include "clang/Edit/Commit.h" 00021 #include "clang/Lex/Lexer.h" 00022 00023 using namespace clang; 00024 using namespace edit; 00025 00026 static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, 00027 IdentifierInfo *&ClassId, 00028 const LangOptions &LangOpts) { 00029 if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) 00030 return false; 00031 00032 const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); 00033 if (!Receiver) 00034 return false; 00035 ClassId = Receiver->getIdentifier(); 00036 00037 if (Msg->getReceiverKind() == ObjCMessageExpr::Class) 00038 return true; 00039 00040 // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, 00041 // since the change from +1 to +0 will be handled fine by ARC. 00042 if (LangOpts.ObjCAutoRefCount) { 00043 if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { 00044 if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( 00045 Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { 00046 if (Rec->getMethodFamily() == OMF_alloc) 00047 return true; 00048 } 00049 } 00050 } 00051 00052 return false; 00053 } 00054 00055 //===----------------------------------------------------------------------===// 00056 // rewriteObjCRedundantCallWithLiteral. 00057 //===----------------------------------------------------------------------===// 00058 00059 bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, 00060 const NSAPI &NS, Commit &commit) { 00061 IdentifierInfo *II = nullptr; 00062 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 00063 return false; 00064 if (Msg->getNumArgs() != 1) 00065 return false; 00066 00067 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 00068 Selector Sel = Msg->getSelector(); 00069 00070 if ((isa<ObjCStringLiteral>(Arg) && 00071 NS.getNSClassId(NSAPI::ClassId_NSString) == II && 00072 (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || 00073 NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || 00074 00075 (isa<ObjCArrayLiteral>(Arg) && 00076 NS.getNSClassId(NSAPI::ClassId_NSArray) == II && 00077 (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || 00078 NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || 00079 00080 (isa<ObjCDictionaryLiteral>(Arg) && 00081 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && 00082 (NS.getNSDictionarySelector( 00083 NSAPI::NSDict_dictionaryWithDictionary) == Sel || 00084 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { 00085 00086 commit.replaceWithInner(Msg->getSourceRange(), 00087 Msg->getArg(0)->getSourceRange()); 00088 return true; 00089 } 00090 00091 return false; 00092 } 00093 00094 //===----------------------------------------------------------------------===// 00095 // rewriteToObjCSubscriptSyntax. 00096 //===----------------------------------------------------------------------===// 00097 00098 /// \brief Check for classes that accept 'objectForKey:' (or the other selectors 00099 /// that the migrator handles) but return their instances as 'id', resulting 00100 /// in the compiler resolving 'objectForKey:' as the method from NSDictionary. 00101 /// 00102 /// When checking if we can convert to subscripting syntax, check whether 00103 /// the receiver is a result of a class method from a hardcoded list of 00104 /// such classes. In such a case return the specific class as the interface 00105 /// of the receiver. 00106 /// 00107 /// FIXME: Remove this when these classes start using 'instancetype'. 00108 static const ObjCInterfaceDecl * 00109 maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, 00110 const Expr *Receiver, 00111 ASTContext &Ctx) { 00112 assert(IFace && Receiver); 00113 00114 // If the receiver has type 'id'... 00115 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) 00116 return IFace; 00117 00118 const ObjCMessageExpr * 00119 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); 00120 if (!InnerMsg) 00121 return IFace; 00122 00123 QualType ClassRec; 00124 switch (InnerMsg->getReceiverKind()) { 00125 case ObjCMessageExpr::Instance: 00126 case ObjCMessageExpr::SuperInstance: 00127 return IFace; 00128 00129 case ObjCMessageExpr::Class: 00130 ClassRec = InnerMsg->getClassReceiver(); 00131 break; 00132 case ObjCMessageExpr::SuperClass: 00133 ClassRec = InnerMsg->getSuperType(); 00134 break; 00135 } 00136 00137 if (ClassRec.isNull()) 00138 return IFace; 00139 00140 // ...and it is the result of a class message... 00141 00142 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); 00143 if (!ObjTy) 00144 return IFace; 00145 const ObjCInterfaceDecl *OID = ObjTy->getInterface(); 00146 00147 // ...and the receiving class is NSMapTable or NSLocale, return that 00148 // class as the receiving interface. 00149 if (OID->getName() == "NSMapTable" || 00150 OID->getName() == "NSLocale") 00151 return OID; 00152 00153 return IFace; 00154 } 00155 00156 static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, 00157 const ObjCMessageExpr *Msg, 00158 ASTContext &Ctx, 00159 Selector subscriptSel) { 00160 const Expr *Rec = Msg->getInstanceReceiver(); 00161 if (!Rec) 00162 return false; 00163 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); 00164 00165 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { 00166 if (!MD->isUnavailable()) 00167 return true; 00168 } 00169 return false; 00170 } 00171 00172 static bool subscriptOperatorNeedsParens(const Expr *FullExpr); 00173 00174 static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 00175 if (subscriptOperatorNeedsParens(Receiver)) { 00176 SourceRange RecRange = Receiver->getSourceRange(); 00177 commit.insertWrap("(", RecRange, ")"); 00178 } 00179 } 00180 00181 static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 00182 Commit &commit) { 00183 if (Msg->getNumArgs() != 1) 00184 return false; 00185 const Expr *Rec = Msg->getInstanceReceiver(); 00186 if (!Rec) 00187 return false; 00188 00189 SourceRange MsgRange = Msg->getSourceRange(); 00190 SourceRange RecRange = Rec->getSourceRange(); 00191 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 00192 00193 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 00194 ArgRange.getBegin()), 00195 CharSourceRange::getTokenRange(RecRange)); 00196 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 00197 ArgRange); 00198 commit.insertWrap("[", ArgRange, "]"); 00199 maybePutParensOnReceiver(Rec, commit); 00200 return true; 00201 } 00202 00203 static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 00204 const ObjCMessageExpr *Msg, 00205 const NSAPI &NS, 00206 Commit &commit) { 00207 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 00208 NS.getObjectAtIndexedSubscriptSelector())) 00209 return false; 00210 return rewriteToSubscriptGetCommon(Msg, commit); 00211 } 00212 00213 static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 00214 const ObjCMessageExpr *Msg, 00215 const NSAPI &NS, 00216 Commit &commit) { 00217 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 00218 NS.getObjectForKeyedSubscriptSelector())) 00219 return false; 00220 return rewriteToSubscriptGetCommon(Msg, commit); 00221 } 00222 00223 static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 00224 const ObjCMessageExpr *Msg, 00225 const NSAPI &NS, 00226 Commit &commit) { 00227 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 00228 NS.getSetObjectAtIndexedSubscriptSelector())) 00229 return false; 00230 00231 if (Msg->getNumArgs() != 2) 00232 return false; 00233 const Expr *Rec = Msg->getInstanceReceiver(); 00234 if (!Rec) 00235 return false; 00236 00237 SourceRange MsgRange = Msg->getSourceRange(); 00238 SourceRange RecRange = Rec->getSourceRange(); 00239 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 00240 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 00241 00242 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 00243 Arg0Range.getBegin()), 00244 CharSourceRange::getTokenRange(RecRange)); 00245 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 00246 Arg1Range.getBegin()), 00247 CharSourceRange::getTokenRange(Arg0Range)); 00248 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 00249 Arg1Range); 00250 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 00251 Arg1Range.getBegin()), 00252 "] = "); 00253 maybePutParensOnReceiver(Rec, commit); 00254 return true; 00255 } 00256 00257 static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 00258 const ObjCMessageExpr *Msg, 00259 const NSAPI &NS, 00260 Commit &commit) { 00261 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 00262 NS.getSetObjectForKeyedSubscriptSelector())) 00263 return false; 00264 00265 if (Msg->getNumArgs() != 2) 00266 return false; 00267 const Expr *Rec = Msg->getInstanceReceiver(); 00268 if (!Rec) 00269 return false; 00270 00271 SourceRange MsgRange = Msg->getSourceRange(); 00272 SourceRange RecRange = Rec->getSourceRange(); 00273 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 00274 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 00275 00276 SourceLocation LocBeforeVal = Arg0Range.getBegin(); 00277 commit.insertBefore(LocBeforeVal, "] = "); 00278 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 00279 /*beforePreviousInsertions=*/true); 00280 commit.insertBefore(LocBeforeVal, "["); 00281 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 00282 Arg0Range.getBegin()), 00283 CharSourceRange::getTokenRange(RecRange)); 00284 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 00285 Arg0Range); 00286 maybePutParensOnReceiver(Rec, commit); 00287 return true; 00288 } 00289 00290 bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 00291 const NSAPI &NS, Commit &commit) { 00292 if (!Msg || Msg->isImplicit() || 00293 Msg->getReceiverKind() != ObjCMessageExpr::Instance) 00294 return false; 00295 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 00296 if (!Method) 00297 return false; 00298 00299 const ObjCInterfaceDecl *IFace = 00300 NS.getASTContext().getObjContainingInterface(Method); 00301 if (!IFace) 00302 return false; 00303 Selector Sel = Msg->getSelector(); 00304 00305 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 00306 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 00307 00308 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 00309 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 00310 00311 if (Msg->getNumArgs() != 2) 00312 return false; 00313 00314 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 00315 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 00316 00317 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 00318 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 00319 00320 return false; 00321 } 00322 00323 //===----------------------------------------------------------------------===// 00324 // rewriteToObjCLiteralSyntax. 00325 //===----------------------------------------------------------------------===// 00326 00327 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 00328 const NSAPI &NS, Commit &commit, 00329 const ParentMap *PMap); 00330 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 00331 const NSAPI &NS, Commit &commit); 00332 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 00333 const NSAPI &NS, Commit &commit); 00334 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 00335 const NSAPI &NS, Commit &commit); 00336 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 00337 const NSAPI &NS, Commit &commit); 00338 00339 bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 00340 const NSAPI &NS, Commit &commit, 00341 const ParentMap *PMap) { 00342 IdentifierInfo *II = nullptr; 00343 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 00344 return false; 00345 00346 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 00347 return rewriteToArrayLiteral(Msg, NS, commit, PMap); 00348 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 00349 return rewriteToDictionaryLiteral(Msg, NS, commit); 00350 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 00351 return rewriteToNumberLiteral(Msg, NS, commit); 00352 if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 00353 return rewriteToStringBoxedExpression(Msg, NS, commit); 00354 00355 return false; 00356 } 00357 00358 /// \brief Returns true if the immediate message arguments of \c Msg should not 00359 /// be rewritten because it will interfere with the rewrite of the parent 00360 /// message expression. e.g. 00361 /// \code 00362 /// [NSDictionary dictionaryWithObjects: 00363 /// [NSArray arrayWithObjects:@"1", @"2", nil] 00364 /// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; 00365 /// \endcode 00366 /// It will return true for this because we are going to rewrite this directly 00367 /// to a dictionary literal without any array literals. 00368 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 00369 const NSAPI &NS); 00370 00371 //===----------------------------------------------------------------------===// 00372 // rewriteToArrayLiteral. 00373 //===----------------------------------------------------------------------===// 00374 00375 /// \brief Adds an explicit cast to 'id' if the type is not objc object. 00376 static void objectifyExpr(const Expr *E, Commit &commit); 00377 00378 static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 00379 const NSAPI &NS, Commit &commit, 00380 const ParentMap *PMap) { 00381 if (PMap) { 00382 const ObjCMessageExpr *ParentMsg = 00383 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); 00384 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) 00385 return false; 00386 } 00387 00388 Selector Sel = Msg->getSelector(); 00389 SourceRange MsgRange = Msg->getSourceRange(); 00390 00391 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 00392 if (Msg->getNumArgs() != 0) 00393 return false; 00394 commit.replace(MsgRange, "@[]"); 00395 return true; 00396 } 00397 00398 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 00399 if (Msg->getNumArgs() != 1) 00400 return false; 00401 objectifyExpr(Msg->getArg(0), commit); 00402 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 00403 commit.replaceWithInner(MsgRange, ArgRange); 00404 commit.insertWrap("@[", ArgRange, "]"); 00405 return true; 00406 } 00407 00408 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 00409 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 00410 if (Msg->getNumArgs() == 0) 00411 return false; 00412 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 00413 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 00414 return false; 00415 00416 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 00417 objectifyExpr(Msg->getArg(i), commit); 00418 00419 if (Msg->getNumArgs() == 1) { 00420 commit.replace(MsgRange, "@[]"); 00421 return true; 00422 } 00423 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 00424 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 00425 commit.replaceWithInner(MsgRange, ArgRange); 00426 commit.insertWrap("@[", ArgRange, "]"); 00427 return true; 00428 } 00429 00430 return false; 00431 } 00432 00433 //===----------------------------------------------------------------------===// 00434 // rewriteToDictionaryLiteral. 00435 //===----------------------------------------------------------------------===// 00436 00437 /// \brief If \c Msg is an NSArray creation message or literal, this gets the 00438 /// objects that were used to create it. 00439 /// \returns true if it is an NSArray and we got objects, or false otherwise. 00440 static bool getNSArrayObjects(const Expr *E, const NSAPI &NS, 00441 SmallVectorImpl<const Expr *> &Objs) { 00442 if (!E) 00443 return false; 00444 00445 E = E->IgnoreParenCasts(); 00446 if (!E) 00447 return false; 00448 00449 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { 00450 IdentifierInfo *Cls = nullptr; 00451 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) 00452 return false; 00453 00454 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) 00455 return false; 00456 00457 Selector Sel = Msg->getSelector(); 00458 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) 00459 return true; // empty array. 00460 00461 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 00462 if (Msg->getNumArgs() != 1) 00463 return false; 00464 Objs.push_back(Msg->getArg(0)); 00465 return true; 00466 } 00467 00468 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 00469 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 00470 if (Msg->getNumArgs() == 0) 00471 return false; 00472 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 00473 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 00474 return false; 00475 00476 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 00477 Objs.push_back(Msg->getArg(i)); 00478 return true; 00479 } 00480 00481 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { 00482 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) 00483 Objs.push_back(ArrLit->getElement(i)); 00484 return true; 00485 } 00486 00487 return false; 00488 } 00489 00490 static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 00491 const NSAPI &NS, Commit &commit) { 00492 Selector Sel = Msg->getSelector(); 00493 SourceRange MsgRange = Msg->getSourceRange(); 00494 00495 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 00496 if (Msg->getNumArgs() != 0) 00497 return false; 00498 commit.replace(MsgRange, "@{}"); 00499 return true; 00500 } 00501 00502 if (Sel == NS.getNSDictionarySelector( 00503 NSAPI::NSDict_dictionaryWithObjectForKey)) { 00504 if (Msg->getNumArgs() != 2) 00505 return false; 00506 00507 objectifyExpr(Msg->getArg(0), commit); 00508 objectifyExpr(Msg->getArg(1), commit); 00509 00510 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 00511 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 00512 // Insert key before the value. 00513 commit.insertBefore(ValRange.getBegin(), ": "); 00514 commit.insertFromRange(ValRange.getBegin(), 00515 CharSourceRange::getTokenRange(KeyRange), 00516 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 00517 commit.insertBefore(ValRange.getBegin(), "@{"); 00518 commit.insertAfterToken(ValRange.getEnd(), "}"); 00519 commit.replaceWithInner(MsgRange, ValRange); 00520 return true; 00521 } 00522 00523 if (Sel == NS.getNSDictionarySelector( 00524 NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 00525 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 00526 if (Msg->getNumArgs() % 2 != 1) 00527 return false; 00528 unsigned SentinelIdx = Msg->getNumArgs() - 1; 00529 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 00530 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 00531 return false; 00532 00533 if (Msg->getNumArgs() == 1) { 00534 commit.replace(MsgRange, "@{}"); 00535 return true; 00536 } 00537 00538 for (unsigned i = 0; i < SentinelIdx; i += 2) { 00539 objectifyExpr(Msg->getArg(i), commit); 00540 objectifyExpr(Msg->getArg(i+1), commit); 00541 00542 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 00543 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 00544 // Insert value after key. 00545 commit.insertAfterToken(KeyRange.getEnd(), ": "); 00546 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 00547 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 00548 KeyRange.getBegin())); 00549 } 00550 // Range of arguments up until and including the last key. 00551 // The sentinel and first value are cut off, the value will move after the 00552 // key. 00553 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 00554 Msg->getArg(SentinelIdx-1)->getLocEnd()); 00555 commit.insertWrap("@{", ArgRange, "}"); 00556 commit.replaceWithInner(MsgRange, ArgRange); 00557 return true; 00558 } 00559 00560 if (Sel == NS.getNSDictionarySelector( 00561 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 00562 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 00563 if (Msg->getNumArgs() != 2) 00564 return false; 00565 00566 SmallVector<const Expr *, 8> Vals; 00567 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 00568 return false; 00569 00570 SmallVector<const Expr *, 8> Keys; 00571 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 00572 return false; 00573 00574 if (Vals.size() != Keys.size()) 00575 return false; 00576 00577 if (Vals.empty()) { 00578 commit.replace(MsgRange, "@{}"); 00579 return true; 00580 } 00581 00582 for (unsigned i = 0, n = Vals.size(); i < n; ++i) { 00583 objectifyExpr(Vals[i], commit); 00584 objectifyExpr(Keys[i], commit); 00585 00586 SourceRange ValRange = Vals[i]->getSourceRange(); 00587 SourceRange KeyRange = Keys[i]->getSourceRange(); 00588 // Insert value after key. 00589 commit.insertAfterToken(KeyRange.getEnd(), ": "); 00590 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 00591 } 00592 // Range of arguments up until and including the last key. 00593 // The first value is cut off, the value will move after the key. 00594 SourceRange ArgRange(Keys.front()->getLocStart(), 00595 Keys.back()->getLocEnd()); 00596 commit.insertWrap("@{", ArgRange, "}"); 00597 commit.replaceWithInner(MsgRange, ArgRange); 00598 return true; 00599 } 00600 00601 return false; 00602 } 00603 00604 static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 00605 const NSAPI &NS) { 00606 if (!Msg) 00607 return false; 00608 00609 IdentifierInfo *II = nullptr; 00610 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 00611 return false; 00612 00613 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 00614 return false; 00615 00616 Selector Sel = Msg->getSelector(); 00617 if (Sel == NS.getNSDictionarySelector( 00618 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 00619 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 00620 if (Msg->getNumArgs() != 2) 00621 return false; 00622 00623 SmallVector<const Expr *, 8> Vals; 00624 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 00625 return false; 00626 00627 SmallVector<const Expr *, 8> Keys; 00628 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 00629 return false; 00630 00631 if (Vals.size() != Keys.size()) 00632 return false; 00633 00634 return true; 00635 } 00636 00637 return false; 00638 } 00639 00640 //===----------------------------------------------------------------------===// 00641 // rewriteToNumberLiteral. 00642 //===----------------------------------------------------------------------===// 00643 00644 static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 00645 const CharacterLiteral *Arg, 00646 const NSAPI &NS, Commit &commit) { 00647 if (Arg->getKind() != CharacterLiteral::Ascii) 00648 return false; 00649 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 00650 Msg->getSelector())) { 00651 SourceRange ArgRange = Arg->getSourceRange(); 00652 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 00653 commit.insert(ArgRange.getBegin(), "@"); 00654 return true; 00655 } 00656 00657 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00658 } 00659 00660 static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 00661 const Expr *Arg, 00662 const NSAPI &NS, Commit &commit) { 00663 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 00664 Msg->getSelector())) { 00665 SourceRange ArgRange = Arg->getSourceRange(); 00666 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 00667 commit.insert(ArgRange.getBegin(), "@"); 00668 return true; 00669 } 00670 00671 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00672 } 00673 00674 namespace { 00675 00676 struct LiteralInfo { 00677 bool Hex, Octal; 00678 StringRef U, F, L, LL; 00679 CharSourceRange WithoutSuffRange; 00680 }; 00681 00682 } 00683 00684 static bool getLiteralInfo(SourceRange literalRange, 00685 bool isFloat, bool isIntZero, 00686 ASTContext &Ctx, LiteralInfo &Info) { 00687 if (literalRange.getBegin().isMacroID() || 00688 literalRange.getEnd().isMacroID()) 00689 return false; 00690 StringRef text = Lexer::getSourceText( 00691 CharSourceRange::getTokenRange(literalRange), 00692 Ctx.getSourceManager(), Ctx.getLangOpts()); 00693 if (text.empty()) 00694 return false; 00695 00696 Optional<bool> UpperU, UpperL; 00697 bool UpperF = false; 00698 00699 struct Suff { 00700 static bool has(StringRef suff, StringRef &text) { 00701 if (text.endswith(suff)) { 00702 text = text.substr(0, text.size()-suff.size()); 00703 return true; 00704 } 00705 return false; 00706 } 00707 }; 00708 00709 while (1) { 00710 if (Suff::has("u", text)) { 00711 UpperU = false; 00712 } else if (Suff::has("U", text)) { 00713 UpperU = true; 00714 } else if (Suff::has("ll", text)) { 00715 UpperL = false; 00716 } else if (Suff::has("LL", text)) { 00717 UpperL = true; 00718 } else if (Suff::has("l", text)) { 00719 UpperL = false; 00720 } else if (Suff::has("L", text)) { 00721 UpperL = true; 00722 } else if (isFloat && Suff::has("f", text)) { 00723 UpperF = false; 00724 } else if (isFloat && Suff::has("F", text)) { 00725 UpperF = true; 00726 } else 00727 break; 00728 } 00729 00730 if (!UpperU.hasValue() && !UpperL.hasValue()) 00731 UpperU = UpperL = true; 00732 else if (UpperU.hasValue() && !UpperL.hasValue()) 00733 UpperL = UpperU; 00734 else if (UpperL.hasValue() && !UpperU.hasValue()) 00735 UpperU = UpperL; 00736 00737 Info.U = *UpperU ? "U" : "u"; 00738 Info.L = *UpperL ? "L" : "l"; 00739 Info.LL = *UpperL ? "LL" : "ll"; 00740 Info.F = UpperF ? "F" : "f"; 00741 00742 Info.Hex = Info.Octal = false; 00743 if (text.startswith("0x")) 00744 Info.Hex = true; 00745 else if (!isFloat && !isIntZero && text.startswith("0")) 00746 Info.Octal = true; 00747 00748 SourceLocation B = literalRange.getBegin(); 00749 Info.WithoutSuffRange = 00750 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 00751 return true; 00752 } 00753 00754 static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 00755 const NSAPI &NS, Commit &commit) { 00756 if (Msg->getNumArgs() != 1) 00757 return false; 00758 00759 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 00760 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 00761 return rewriteToCharLiteral(Msg, CharE, NS, commit); 00762 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 00763 return rewriteToBoolLiteral(Msg, BE, NS, commit); 00764 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 00765 return rewriteToBoolLiteral(Msg, BE, NS, commit); 00766 00767 const Expr *literalE = Arg; 00768 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 00769 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 00770 literalE = UOE->getSubExpr(); 00771 } 00772 00773 // Only integer and floating literals, otherwise try to rewrite to boxed 00774 // expression. 00775 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 00776 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00777 00778 ASTContext &Ctx = NS.getASTContext(); 00779 Selector Sel = Msg->getSelector(); 00780 Optional<NSAPI::NSNumberLiteralMethodKind> 00781 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 00782 if (!MKOpt) 00783 return false; 00784 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 00785 00786 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 00787 bool CallIsFloating = false, CallIsDouble = false; 00788 00789 switch (MK) { 00790 // We cannot have these calls with int/float literals. 00791 case NSAPI::NSNumberWithChar: 00792 case NSAPI::NSNumberWithUnsignedChar: 00793 case NSAPI::NSNumberWithShort: 00794 case NSAPI::NSNumberWithUnsignedShort: 00795 case NSAPI::NSNumberWithBool: 00796 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00797 00798 case NSAPI::NSNumberWithUnsignedInt: 00799 case NSAPI::NSNumberWithUnsignedInteger: 00800 CallIsUnsigned = true; 00801 case NSAPI::NSNumberWithInt: 00802 case NSAPI::NSNumberWithInteger: 00803 break; 00804 00805 case NSAPI::NSNumberWithUnsignedLong: 00806 CallIsUnsigned = true; 00807 case NSAPI::NSNumberWithLong: 00808 CallIsLong = true; 00809 break; 00810 00811 case NSAPI::NSNumberWithUnsignedLongLong: 00812 CallIsUnsigned = true; 00813 case NSAPI::NSNumberWithLongLong: 00814 CallIsLongLong = true; 00815 break; 00816 00817 case NSAPI::NSNumberWithDouble: 00818 CallIsDouble = true; 00819 case NSAPI::NSNumberWithFloat: 00820 CallIsFloating = true; 00821 break; 00822 } 00823 00824 SourceRange ArgRange = Arg->getSourceRange(); 00825 QualType ArgTy = Arg->getType(); 00826 QualType CallTy = Msg->getArg(0)->getType(); 00827 00828 // Check for the easy case, the literal maps directly to the call. 00829 if (Ctx.hasSameType(ArgTy, CallTy)) { 00830 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 00831 commit.insert(ArgRange.getBegin(), "@"); 00832 return true; 00833 } 00834 00835 // We will need to modify the literal suffix to get the same type as the call. 00836 // Try with boxed expression if it came from a macro. 00837 if (ArgRange.getBegin().isMacroID()) 00838 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00839 00840 bool LitIsFloat = ArgTy->isFloatingType(); 00841 // For a float passed to integer call, don't try rewriting to objc literal. 00842 // It is difficult and a very uncommon case anyway. 00843 // But try with boxed expression. 00844 if (LitIsFloat && !CallIsFloating) 00845 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00846 00847 // Try to modify the literal make it the same type as the method call. 00848 // -Modify the suffix, and/or 00849 // -Change integer to float 00850 00851 LiteralInfo LitInfo; 00852 bool isIntZero = false; 00853 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 00854 isIntZero = !IntE->getValue().getBoolValue(); 00855 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 00856 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00857 00858 // Not easy to do int -> float with hex/octal and uncommon anyway. 00859 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 00860 return rewriteToNumericBoxedExpression(Msg, NS, commit); 00861 00862 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 00863 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 00864 00865 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 00866 LitInfo.WithoutSuffRange); 00867 commit.insert(LitB, "@"); 00868 00869 if (!LitIsFloat && CallIsFloating) 00870 commit.insert(LitE, ".0"); 00871 00872 if (CallIsFloating) { 00873 if (!CallIsDouble) 00874 commit.insert(LitE, LitInfo.F); 00875 } else { 00876 if (CallIsUnsigned) 00877 commit.insert(LitE, LitInfo.U); 00878 00879 if (CallIsLong) 00880 commit.insert(LitE, LitInfo.L); 00881 else if (CallIsLongLong) 00882 commit.insert(LitE, LitInfo.LL); 00883 } 00884 return true; 00885 } 00886 00887 // FIXME: Make determination of operator precedence more general and 00888 // make it broadly available. 00889 static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 00890 const Expr* Expr = FullExpr->IgnoreImpCasts(); 00891 if (isa<ArraySubscriptExpr>(Expr) || 00892 isa<CallExpr>(Expr) || 00893 isa<DeclRefExpr>(Expr) || 00894 isa<CXXNamedCastExpr>(Expr) || 00895 isa<CXXConstructExpr>(Expr) || 00896 isa<CXXThisExpr>(Expr) || 00897 isa<CXXTypeidExpr>(Expr) || 00898 isa<CXXUnresolvedConstructExpr>(Expr) || 00899 isa<ObjCMessageExpr>(Expr) || 00900 isa<ObjCPropertyRefExpr>(Expr) || 00901 isa<ObjCProtocolExpr>(Expr) || 00902 isa<MemberExpr>(Expr) || 00903 isa<ObjCIvarRefExpr>(Expr) || 00904 isa<ParenExpr>(FullExpr) || 00905 isa<ParenListExpr>(Expr) || 00906 isa<SizeOfPackExpr>(Expr)) 00907 return false; 00908 00909 return true; 00910 } 00911 static bool castOperatorNeedsParens(const Expr *FullExpr) { 00912 const Expr* Expr = FullExpr->IgnoreImpCasts(); 00913 if (isa<ArraySubscriptExpr>(Expr) || 00914 isa<CallExpr>(Expr) || 00915 isa<DeclRefExpr>(Expr) || 00916 isa<CastExpr>(Expr) || 00917 isa<CXXNewExpr>(Expr) || 00918 isa<CXXConstructExpr>(Expr) || 00919 isa<CXXDeleteExpr>(Expr) || 00920 isa<CXXNoexceptExpr>(Expr) || 00921 isa<CXXPseudoDestructorExpr>(Expr) || 00922 isa<CXXScalarValueInitExpr>(Expr) || 00923 isa<CXXThisExpr>(Expr) || 00924 isa<CXXTypeidExpr>(Expr) || 00925 isa<CXXUnresolvedConstructExpr>(Expr) || 00926 isa<ObjCMessageExpr>(Expr) || 00927 isa<ObjCPropertyRefExpr>(Expr) || 00928 isa<ObjCProtocolExpr>(Expr) || 00929 isa<MemberExpr>(Expr) || 00930 isa<ObjCIvarRefExpr>(Expr) || 00931 isa<ParenExpr>(FullExpr) || 00932 isa<ParenListExpr>(Expr) || 00933 isa<SizeOfPackExpr>(Expr) || 00934 isa<UnaryOperator>(Expr)) 00935 return false; 00936 00937 return true; 00938 } 00939 00940 static void objectifyExpr(const Expr *E, Commit &commit) { 00941 if (!E) return; 00942 00943 QualType T = E->getType(); 00944 if (T->isObjCObjectPointerType()) { 00945 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 00946 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 00947 return; 00948 } else { 00949 return; 00950 } 00951 } else if (!T->isPointerType()) { 00952 return; 00953 } 00954 00955 SourceRange Range = E->getSourceRange(); 00956 if (castOperatorNeedsParens(E)) 00957 commit.insertWrap("(", Range, ")"); 00958 commit.insertBefore(Range.getBegin(), "(id)"); 00959 } 00960 00961 //===----------------------------------------------------------------------===// 00962 // rewriteToNumericBoxedExpression. 00963 //===----------------------------------------------------------------------===// 00964 00965 static bool isEnumConstant(const Expr *E) { 00966 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 00967 if (const ValueDecl *VD = DRE->getDecl()) 00968 return isa<EnumConstantDecl>(VD); 00969 00970 return false; 00971 } 00972 00973 static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 00974 const NSAPI &NS, Commit &commit) { 00975 if (Msg->getNumArgs() != 1) 00976 return false; 00977 00978 const Expr *Arg = Msg->getArg(0); 00979 if (Arg->isTypeDependent()) 00980 return false; 00981 00982 ASTContext &Ctx = NS.getASTContext(); 00983 Selector Sel = Msg->getSelector(); 00984 Optional<NSAPI::NSNumberLiteralMethodKind> 00985 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 00986 if (!MKOpt) 00987 return false; 00988 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 00989 00990 const Expr *OrigArg = Arg->IgnoreImpCasts(); 00991 QualType FinalTy = Arg->getType(); 00992 QualType OrigTy = OrigArg->getType(); 00993 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 00994 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 00995 00996 bool isTruncated = FinalTySize < OrigTySize; 00997 bool needsCast = false; 00998 00999 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 01000 switch (ICE->getCastKind()) { 01001 case CK_LValueToRValue: 01002 case CK_NoOp: 01003 case CK_UserDefinedConversion: 01004 break; 01005 01006 case CK_IntegralCast: { 01007 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 01008 break; 01009 // Be more liberal with Integer/UnsignedInteger which are very commonly 01010 // used. 01011 if ((MK == NSAPI::NSNumberWithInteger || 01012 MK == NSAPI::NSNumberWithUnsignedInteger) && 01013 !isTruncated) { 01014 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 01015 break; 01016 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 01017 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 01018 break; 01019 } 01020 01021 needsCast = true; 01022 break; 01023 } 01024 01025 case CK_PointerToBoolean: 01026 case CK_IntegralToBoolean: 01027 case CK_IntegralToFloating: 01028 case CK_FloatingToIntegral: 01029 case CK_FloatingToBoolean: 01030 case CK_FloatingCast: 01031 case CK_FloatingComplexToReal: 01032 case CK_FloatingComplexToBoolean: 01033 case CK_IntegralComplexToReal: 01034 case CK_IntegralComplexToBoolean: 01035 case CK_AtomicToNonAtomic: 01036 case CK_AddressSpaceConversion: 01037 needsCast = true; 01038 break; 01039 01040 case CK_Dependent: 01041 case CK_BitCast: 01042 case CK_LValueBitCast: 01043 case CK_BaseToDerived: 01044 case CK_DerivedToBase: 01045 case CK_UncheckedDerivedToBase: 01046 case CK_Dynamic: 01047 case CK_ToUnion: 01048 case CK_ArrayToPointerDecay: 01049 case CK_FunctionToPointerDecay: 01050 case CK_NullToPointer: 01051 case CK_NullToMemberPointer: 01052 case CK_BaseToDerivedMemberPointer: 01053 case CK_DerivedToBaseMemberPointer: 01054 case CK_MemberPointerToBoolean: 01055 case CK_ReinterpretMemberPointer: 01056 case CK_ConstructorConversion: 01057 case CK_IntegralToPointer: 01058 case CK_PointerToIntegral: 01059 case CK_ToVoid: 01060 case CK_VectorSplat: 01061 case CK_CPointerToObjCPointerCast: 01062 case CK_BlockPointerToObjCPointerCast: 01063 case CK_AnyPointerToBlockPointerCast: 01064 case CK_ObjCObjectLValueCast: 01065 case CK_FloatingRealToComplex: 01066 case CK_FloatingComplexCast: 01067 case CK_FloatingComplexToIntegralComplex: 01068 case CK_IntegralRealToComplex: 01069 case CK_IntegralComplexCast: 01070 case CK_IntegralComplexToFloatingComplex: 01071 case CK_ARCProduceObject: 01072 case CK_ARCConsumeObject: 01073 case CK_ARCReclaimReturnedObject: 01074 case CK_ARCExtendBlockObject: 01075 case CK_NonAtomicToAtomic: 01076 case CK_CopyAndAutoreleaseBlockObject: 01077 case CK_BuiltinFnToFnPtr: 01078 case CK_ZeroToOCLEvent: 01079 return false; 01080 } 01081 } 01082 01083 if (needsCast) { 01084 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 01085 // FIXME: Use a custom category name to distinguish migration diagnostics. 01086 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 01087 "converting to boxing syntax requires casting %0 to %1"); 01088 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 01089 << Msg->getSourceRange(); 01090 return false; 01091 } 01092 01093 SourceRange ArgRange = OrigArg->getSourceRange(); 01094 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 01095 01096 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 01097 commit.insertBefore(ArgRange.getBegin(), "@"); 01098 else 01099 commit.insertWrap("@(", ArgRange, ")"); 01100 01101 return true; 01102 } 01103 01104 //===----------------------------------------------------------------------===// 01105 // rewriteToStringBoxedExpression. 01106 //===----------------------------------------------------------------------===// 01107 01108 static bool doRewriteToUTF8StringBoxedExpressionHelper( 01109 const ObjCMessageExpr *Msg, 01110 const NSAPI &NS, Commit &commit) { 01111 const Expr *Arg = Msg->getArg(0); 01112 if (Arg->isTypeDependent()) 01113 return false; 01114 01115 ASTContext &Ctx = NS.getASTContext(); 01116 01117 const Expr *OrigArg = Arg->IgnoreImpCasts(); 01118 QualType OrigTy = OrigArg->getType(); 01119 if (OrigTy->isArrayType()) 01120 OrigTy = Ctx.getArrayDecayedType(OrigTy); 01121 01122 if (const StringLiteral * 01123 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 01124 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 01125 commit.insert(StrE->getLocStart(), "@"); 01126 return true; 01127 } 01128 01129 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 01130 QualType PointeeType = PT->getPointeeType(); 01131 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 01132 SourceRange ArgRange = OrigArg->getSourceRange(); 01133 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 01134 01135 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 01136 commit.insertBefore(ArgRange.getBegin(), "@"); 01137 else 01138 commit.insertWrap("@(", ArgRange, ")"); 01139 01140 return true; 01141 } 01142 } 01143 01144 return false; 01145 } 01146 01147 static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 01148 const NSAPI &NS, Commit &commit) { 01149 Selector Sel = Msg->getSelector(); 01150 01151 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 01152 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString) || 01153 Sel == NS.getNSStringSelector(NSAPI::NSStr_initWithUTF8String)) { 01154 if (Msg->getNumArgs() != 1) 01155 return false; 01156 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 01157 } 01158 01159 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 01160 if (Msg->getNumArgs() != 2) 01161 return false; 01162 01163 const Expr *encodingArg = Msg->getArg(1); 01164 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 01165 NS.isNSASCIIStringEncodingConstant(encodingArg)) 01166 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 01167 } 01168 01169 return false; 01170 }