RewriteObjCFoundationAPI.cpp revision 1921b58a2aba4f5073d6634d476356b6a4ff8815
1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===// 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// The LLVM Compiler Infrastructure 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// This file is distributed under the University of Illinois Open Source 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// License. See LICENSE.TXT for details. 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)//===----------------------------------------------------------------------===// 9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Rewrites legacy method calls to modern syntax. 116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)// 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)//===----------------------------------------------------------------------===// 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "clang/Edit/Rewriters.h" 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/AST/ASTContext.h" 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/AST/ExprCXX.h" 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/AST/ExprObjC.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/AST/NSAPI.h" 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/AST/ParentMap.h" 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/Edit/Commit.h" 21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "clang/Lex/Lexer.h" 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using namespace clang; 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using namespace edit; 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) IdentifierInfo *&ClassId, 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const LangOptions &LangOpts) { 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface(); 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!Receiver) 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ClassId = Receiver->getIdentifier(); 36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (Msg->getReceiverKind() == ObjCMessageExpr::Class) 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // since the change from +1 to +0 will be handled fine by ARC. 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (LangOpts.ObjCAutoRefCount) { 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (Rec->getMethodFamily() == OMF_alloc) 47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)//===----------------------------------------------------------------------===// 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// rewriteObjCRedundantCallWithLiteral. 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)//===----------------------------------------------------------------------===// 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const NSAPI &NS, Commit &commit) { 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) IdentifierInfo *II = 0; 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (Msg->getNumArgs() != 1) 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Selector Sel = Msg->getSelector(); 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if ((isa<ObjCStringLiteral>(Arg) && 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) NS.getNSClassId(NSAPI::ClassId_NSString) == II && 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (isa<ObjCArrayLiteral>(Arg) && 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) NS.getNSClassId(NSAPI::ClassId_NSArray) == II && 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) (isa<ObjCDictionaryLiteral>(Arg) && 81 NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && 82 (NS.getNSDictionarySelector( 83 NSAPI::NSDict_dictionaryWithDictionary) == Sel || 84 NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { 85 86 commit.replaceWithInner(Msg->getSourceRange(), 87 Msg->getArg(0)->getSourceRange()); 88 return true; 89 } 90 91 return false; 92} 93 94//===----------------------------------------------------------------------===// 95// rewriteToObjCSubscriptSyntax. 96//===----------------------------------------------------------------------===// 97 98/// \brief Check for classes that accept 'objectForKey:' (or the other selectors 99/// that the migrator handles) but return their instances as 'id', resulting 100/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. 101/// 102/// When checking if we can convert to subscripting syntax, check whether 103/// the receiver is a result of a class method from a hardcoded list of 104/// such classes. In such a case return the specific class as the interface 105/// of the receiver. 106/// 107/// FIXME: Remove this when these classes start using 'instancetype'. 108static const ObjCInterfaceDecl * 109maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, 110 const Expr *Receiver, 111 ASTContext &Ctx) { 112 assert(IFace && Receiver); 113 114 // If the receiver has type 'id'... 115 if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) 116 return IFace; 117 118 const ObjCMessageExpr * 119 InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); 120 if (!InnerMsg) 121 return IFace; 122 123 QualType ClassRec; 124 switch (InnerMsg->getReceiverKind()) { 125 case ObjCMessageExpr::Instance: 126 case ObjCMessageExpr::SuperInstance: 127 return IFace; 128 129 case ObjCMessageExpr::Class: 130 ClassRec = InnerMsg->getClassReceiver(); 131 break; 132 case ObjCMessageExpr::SuperClass: 133 ClassRec = InnerMsg->getSuperType(); 134 break; 135 } 136 137 if (ClassRec.isNull()) 138 return IFace; 139 140 // ...and it is the result of a class message... 141 142 const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); 143 if (!ObjTy) 144 return IFace; 145 const ObjCInterfaceDecl *OID = ObjTy->getInterface(); 146 147 // ...and the receiving class is NSMapTable or NSLocale, return that 148 // class as the receiving interface. 149 if (OID->getName() == "NSMapTable" || 150 OID->getName() == "NSLocale") 151 return OID; 152 153 return IFace; 154} 155 156static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, 157 const ObjCMessageExpr *Msg, 158 ASTContext &Ctx, 159 Selector subscriptSel) { 160 const Expr *Rec = Msg->getInstanceReceiver(); 161 if (!Rec) 162 return false; 163 IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); 164 165 if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { 166 if (!MD->isUnavailable()) 167 return true; 168 } 169 return false; 170} 171 172static bool subscriptOperatorNeedsParens(const Expr *FullExpr); 173 174static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { 175 if (subscriptOperatorNeedsParens(Receiver)) { 176 SourceRange RecRange = Receiver->getSourceRange(); 177 commit.insertWrap("(", RecRange, ")"); 178 } 179} 180 181static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, 182 Commit &commit) { 183 if (Msg->getNumArgs() != 1) 184 return false; 185 const Expr *Rec = Msg->getInstanceReceiver(); 186 if (!Rec) 187 return false; 188 189 SourceRange MsgRange = Msg->getSourceRange(); 190 SourceRange RecRange = Rec->getSourceRange(); 191 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 192 193 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 194 ArgRange.getBegin()), 195 CharSourceRange::getTokenRange(RecRange)); 196 commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()), 197 ArgRange); 198 commit.insertWrap("[", ArgRange, "]"); 199 maybePutParensOnReceiver(Rec, commit); 200 return true; 201} 202 203static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, 204 const ObjCMessageExpr *Msg, 205 const NSAPI &NS, 206 Commit &commit) { 207 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 208 NS.getObjectAtIndexedSubscriptSelector())) 209 return false; 210 return rewriteToSubscriptGetCommon(Msg, commit); 211} 212 213static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, 214 const ObjCMessageExpr *Msg, 215 const NSAPI &NS, 216 Commit &commit) { 217 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 218 NS.getObjectForKeyedSubscriptSelector())) 219 return false; 220 return rewriteToSubscriptGetCommon(Msg, commit); 221} 222 223static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, 224 const ObjCMessageExpr *Msg, 225 const NSAPI &NS, 226 Commit &commit) { 227 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 228 NS.getSetObjectAtIndexedSubscriptSelector())) 229 return false; 230 231 if (Msg->getNumArgs() != 2) 232 return false; 233 const Expr *Rec = Msg->getInstanceReceiver(); 234 if (!Rec) 235 return false; 236 237 SourceRange MsgRange = Msg->getSourceRange(); 238 SourceRange RecRange = Rec->getSourceRange(); 239 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 240 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 241 242 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 243 Arg0Range.getBegin()), 244 CharSourceRange::getTokenRange(RecRange)); 245 commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(), 246 Arg1Range.getBegin()), 247 CharSourceRange::getTokenRange(Arg0Range)); 248 commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()), 249 Arg1Range); 250 commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(), 251 Arg1Range.getBegin()), 252 "] = "); 253 maybePutParensOnReceiver(Rec, commit); 254 return true; 255} 256 257static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, 258 const ObjCMessageExpr *Msg, 259 const NSAPI &NS, 260 Commit &commit) { 261 if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), 262 NS.getSetObjectForKeyedSubscriptSelector())) 263 return false; 264 265 if (Msg->getNumArgs() != 2) 266 return false; 267 const Expr *Rec = Msg->getInstanceReceiver(); 268 if (!Rec) 269 return false; 270 271 SourceRange MsgRange = Msg->getSourceRange(); 272 SourceRange RecRange = Rec->getSourceRange(); 273 SourceRange Arg0Range = Msg->getArg(0)->getSourceRange(); 274 SourceRange Arg1Range = Msg->getArg(1)->getSourceRange(); 275 276 SourceLocation LocBeforeVal = Arg0Range.getBegin(); 277 commit.insertBefore(LocBeforeVal, "] = "); 278 commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false, 279 /*beforePreviousInsertions=*/true); 280 commit.insertBefore(LocBeforeVal, "["); 281 commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(), 282 Arg0Range.getBegin()), 283 CharSourceRange::getTokenRange(RecRange)); 284 commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()), 285 Arg0Range); 286 maybePutParensOnReceiver(Rec, commit); 287 return true; 288} 289 290bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, 291 const NSAPI &NS, Commit &commit) { 292 if (!Msg || Msg->isImplicit() || 293 Msg->getReceiverKind() != ObjCMessageExpr::Instance) 294 return false; 295 const ObjCMethodDecl *Method = Msg->getMethodDecl(); 296 if (!Method) 297 return false; 298 299 const ObjCInterfaceDecl *IFace = 300 NS.getASTContext().getObjContainingInterface(Method); 301 if (!IFace) 302 return false; 303 Selector Sel = Msg->getSelector(); 304 305 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) 306 return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); 307 308 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) 309 return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); 310 311 if (Msg->getNumArgs() != 2) 312 return false; 313 314 if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) 315 return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); 316 317 if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) 318 return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); 319 320 return false; 321} 322 323//===----------------------------------------------------------------------===// 324// rewriteToObjCLiteralSyntax. 325//===----------------------------------------------------------------------===// 326 327static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 328 const NSAPI &NS, Commit &commit, 329 const ParentMap *PMap); 330static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 331 const NSAPI &NS, Commit &commit); 332static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 333 const NSAPI &NS, Commit &commit); 334static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 335 const NSAPI &NS, Commit &commit); 336static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 337 const NSAPI &NS, Commit &commit); 338 339bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, 340 const NSAPI &NS, Commit &commit, 341 const ParentMap *PMap) { 342 IdentifierInfo *II = 0; 343 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 344 return false; 345 346 if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) 347 return rewriteToArrayLiteral(Msg, NS, commit, PMap); 348 if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 349 return rewriteToDictionaryLiteral(Msg, NS, commit); 350 if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) 351 return rewriteToNumberLiteral(Msg, NS, commit); 352 if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) 353 return rewriteToStringBoxedExpression(Msg, NS, commit); 354 355 return false; 356} 357 358bool edit::rewriteToObjCProperty(const ObjCMethodDecl *Getter, 359 const ObjCMethodDecl *Setter, 360 const NSAPI &NS, Commit &commit) { 361 std::string PropertyString = "@property"; 362 const ParmVarDecl *argDecl = *Setter->param_begin(); 363 QualType ArgType = argDecl->getType(); 364 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 365 if (propertyLifetime != Qualifiers::OCL_None) { 366 PropertyString += "("; 367 if (propertyLifetime == Qualifiers::OCL_Strong) 368 PropertyString += "strong"; 369 else if (propertyLifetime == Qualifiers::OCL_Weak) 370 PropertyString += "weak"; 371 else 372 PropertyString += "unsafe_unretained"; 373 PropertyString += ")"; 374 } 375 QualType PropQT = Getter->getResultType(); 376 PropertyString += " "; 377 PropertyString += PropQT.getAsString(NS.getASTContext().getPrintingPolicy()); 378 PropertyString += " "; 379 PropertyString += Getter->getNameAsString(); 380 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 381 Getter->getDeclaratorEndLoc()), 382 PropertyString); 383 SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 384 // Get location past ';' 385 EndLoc = EndLoc.getLocWithOffset(1); 386 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc)); 387 return true; 388} 389 390/// \brief Returns true if the immediate message arguments of \c Msg should not 391/// be rewritten because it will interfere with the rewrite of the parent 392/// message expression. e.g. 393/// \code 394/// [NSDictionary dictionaryWithObjects: 395/// [NSArray arrayWithObjects:@"1", @"2", nil] 396/// forKeys:[NSArray arrayWithObjects:@"A", @"B", nil]]; 397/// \endcode 398/// It will return true for this because we are going to rewrite this directly 399/// to a dictionary literal without any array literals. 400static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 401 const NSAPI &NS); 402 403//===----------------------------------------------------------------------===// 404// rewriteToArrayLiteral. 405//===----------------------------------------------------------------------===// 406 407/// \brief Adds an explicit cast to 'id' if the type is not objc object. 408static void objectifyExpr(const Expr *E, Commit &commit); 409 410static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, 411 const NSAPI &NS, Commit &commit, 412 const ParentMap *PMap) { 413 if (PMap) { 414 const ObjCMessageExpr *ParentMsg = 415 dyn_cast_or_null<ObjCMessageExpr>(PMap->getParentIgnoreParenCasts(Msg)); 416 if (shouldNotRewriteImmediateMessageArgs(ParentMsg, NS)) 417 return false; 418 } 419 420 Selector Sel = Msg->getSelector(); 421 SourceRange MsgRange = Msg->getSourceRange(); 422 423 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) { 424 if (Msg->getNumArgs() != 0) 425 return false; 426 commit.replace(MsgRange, "@[]"); 427 return true; 428 } 429 430 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 431 if (Msg->getNumArgs() != 1) 432 return false; 433 objectifyExpr(Msg->getArg(0), commit); 434 SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); 435 commit.replaceWithInner(MsgRange, ArgRange); 436 commit.insertWrap("@[", ArgRange, "]"); 437 return true; 438 } 439 440 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 441 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 442 if (Msg->getNumArgs() == 0) 443 return false; 444 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 445 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 446 return false; 447 448 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 449 objectifyExpr(Msg->getArg(i), commit); 450 451 if (Msg->getNumArgs() == 1) { 452 commit.replace(MsgRange, "@[]"); 453 return true; 454 } 455 SourceRange ArgRange(Msg->getArg(0)->getLocStart(), 456 Msg->getArg(Msg->getNumArgs()-2)->getLocEnd()); 457 commit.replaceWithInner(MsgRange, ArgRange); 458 commit.insertWrap("@[", ArgRange, "]"); 459 return true; 460 } 461 462 return false; 463} 464 465//===----------------------------------------------------------------------===// 466// rewriteToDictionaryLiteral. 467//===----------------------------------------------------------------------===// 468 469/// \brief If \c Msg is an NSArray creation message or literal, this gets the 470/// objects that were used to create it. 471/// \returns true if it is an NSArray and we got objects, or false otherwise. 472static bool getNSArrayObjects(const Expr *E, const NSAPI &NS, 473 SmallVectorImpl<const Expr *> &Objs) { 474 if (!E) 475 return false; 476 477 E = E->IgnoreParenCasts(); 478 if (!E) 479 return false; 480 481 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { 482 IdentifierInfo *Cls = 0; 483 if (!checkForLiteralCreation(Msg, Cls, NS.getASTContext().getLangOpts())) 484 return false; 485 486 if (Cls != NS.getNSClassId(NSAPI::ClassId_NSArray)) 487 return false; 488 489 Selector Sel = Msg->getSelector(); 490 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) 491 return true; // empty array. 492 493 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { 494 if (Msg->getNumArgs() != 1) 495 return false; 496 Objs.push_back(Msg->getArg(0)); 497 return true; 498 } 499 500 if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || 501 Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { 502 if (Msg->getNumArgs() == 0) 503 return false; 504 const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); 505 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 506 return false; 507 508 for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) 509 Objs.push_back(Msg->getArg(i)); 510 return true; 511 } 512 513 } else if (const ObjCArrayLiteral *ArrLit = dyn_cast<ObjCArrayLiteral>(E)) { 514 for (unsigned i = 0, e = ArrLit->getNumElements(); i != e; ++i) 515 Objs.push_back(ArrLit->getElement(i)); 516 return true; 517 } 518 519 return false; 520} 521 522static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, 523 const NSAPI &NS, Commit &commit) { 524 Selector Sel = Msg->getSelector(); 525 SourceRange MsgRange = Msg->getSourceRange(); 526 527 if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) { 528 if (Msg->getNumArgs() != 0) 529 return false; 530 commit.replace(MsgRange, "@{}"); 531 return true; 532 } 533 534 if (Sel == NS.getNSDictionarySelector( 535 NSAPI::NSDict_dictionaryWithObjectForKey)) { 536 if (Msg->getNumArgs() != 2) 537 return false; 538 539 objectifyExpr(Msg->getArg(0), commit); 540 objectifyExpr(Msg->getArg(1), commit); 541 542 SourceRange ValRange = Msg->getArg(0)->getSourceRange(); 543 SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); 544 // Insert key before the value. 545 commit.insertBefore(ValRange.getBegin(), ": "); 546 commit.insertFromRange(ValRange.getBegin(), 547 CharSourceRange::getTokenRange(KeyRange), 548 /*afterToken=*/false, /*beforePreviousInsertions=*/true); 549 commit.insertBefore(ValRange.getBegin(), "@{"); 550 commit.insertAfterToken(ValRange.getEnd(), "}"); 551 commit.replaceWithInner(MsgRange, ValRange); 552 return true; 553 } 554 555 if (Sel == NS.getNSDictionarySelector( 556 NSAPI::NSDict_dictionaryWithObjectsAndKeys) || 557 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { 558 if (Msg->getNumArgs() % 2 != 1) 559 return false; 560 unsigned SentinelIdx = Msg->getNumArgs() - 1; 561 const Expr *SentinelExpr = Msg->getArg(SentinelIdx); 562 if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) 563 return false; 564 565 if (Msg->getNumArgs() == 1) { 566 commit.replace(MsgRange, "@{}"); 567 return true; 568 } 569 570 for (unsigned i = 0; i < SentinelIdx; i += 2) { 571 objectifyExpr(Msg->getArg(i), commit); 572 objectifyExpr(Msg->getArg(i+1), commit); 573 574 SourceRange ValRange = Msg->getArg(i)->getSourceRange(); 575 SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); 576 // Insert value after key. 577 commit.insertAfterToken(KeyRange.getEnd(), ": "); 578 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 579 commit.remove(CharSourceRange::getCharRange(ValRange.getBegin(), 580 KeyRange.getBegin())); 581 } 582 // Range of arguments up until and including the last key. 583 // The sentinel and first value are cut off, the value will move after the 584 // key. 585 SourceRange ArgRange(Msg->getArg(1)->getLocStart(), 586 Msg->getArg(SentinelIdx-1)->getLocEnd()); 587 commit.insertWrap("@{", ArgRange, "}"); 588 commit.replaceWithInner(MsgRange, ArgRange); 589 return true; 590 } 591 592 if (Sel == NS.getNSDictionarySelector( 593 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 594 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 595 if (Msg->getNumArgs() != 2) 596 return false; 597 598 SmallVector<const Expr *, 8> Vals; 599 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 600 return false; 601 602 SmallVector<const Expr *, 8> Keys; 603 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 604 return false; 605 606 if (Vals.size() != Keys.size()) 607 return false; 608 609 if (Vals.empty()) { 610 commit.replace(MsgRange, "@{}"); 611 return true; 612 } 613 614 for (unsigned i = 0, n = Vals.size(); i < n; ++i) { 615 objectifyExpr(Vals[i], commit); 616 objectifyExpr(Keys[i], commit); 617 618 SourceRange ValRange = Vals[i]->getSourceRange(); 619 SourceRange KeyRange = Keys[i]->getSourceRange(); 620 // Insert value after key. 621 commit.insertAfterToken(KeyRange.getEnd(), ": "); 622 commit.insertFromRange(KeyRange.getEnd(), ValRange, /*afterToken=*/true); 623 } 624 // Range of arguments up until and including the last key. 625 // The first value is cut off, the value will move after the key. 626 SourceRange ArgRange(Keys.front()->getLocStart(), 627 Keys.back()->getLocEnd()); 628 commit.insertWrap("@{", ArgRange, "}"); 629 commit.replaceWithInner(MsgRange, ArgRange); 630 return true; 631 } 632 633 return false; 634} 635 636static bool shouldNotRewriteImmediateMessageArgs(const ObjCMessageExpr *Msg, 637 const NSAPI &NS) { 638 if (!Msg) 639 return false; 640 641 IdentifierInfo *II = 0; 642 if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) 643 return false; 644 645 if (II != NS.getNSClassId(NSAPI::ClassId_NSDictionary)) 646 return false; 647 648 Selector Sel = Msg->getSelector(); 649 if (Sel == NS.getNSDictionarySelector( 650 NSAPI::NSDict_dictionaryWithObjectsForKeys) || 651 Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsForKeys)) { 652 if (Msg->getNumArgs() != 2) 653 return false; 654 655 SmallVector<const Expr *, 8> Vals; 656 if (!getNSArrayObjects(Msg->getArg(0), NS, Vals)) 657 return false; 658 659 SmallVector<const Expr *, 8> Keys; 660 if (!getNSArrayObjects(Msg->getArg(1), NS, Keys)) 661 return false; 662 663 if (Vals.size() != Keys.size()) 664 return false; 665 666 return true; 667 } 668 669 return false; 670} 671 672//===----------------------------------------------------------------------===// 673// rewriteToNumberLiteral. 674//===----------------------------------------------------------------------===// 675 676static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, 677 const CharacterLiteral *Arg, 678 const NSAPI &NS, Commit &commit) { 679 if (Arg->getKind() != CharacterLiteral::Ascii) 680 return false; 681 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithChar, 682 Msg->getSelector())) { 683 SourceRange ArgRange = Arg->getSourceRange(); 684 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 685 commit.insert(ArgRange.getBegin(), "@"); 686 return true; 687 } 688 689 return rewriteToNumericBoxedExpression(Msg, NS, commit); 690} 691 692static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, 693 const Expr *Arg, 694 const NSAPI &NS, Commit &commit) { 695 if (NS.isNSNumberLiteralSelector(NSAPI::NSNumberWithBool, 696 Msg->getSelector())) { 697 SourceRange ArgRange = Arg->getSourceRange(); 698 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 699 commit.insert(ArgRange.getBegin(), "@"); 700 return true; 701 } 702 703 return rewriteToNumericBoxedExpression(Msg, NS, commit); 704} 705 706namespace { 707 708struct LiteralInfo { 709 bool Hex, Octal; 710 StringRef U, F, L, LL; 711 CharSourceRange WithoutSuffRange; 712}; 713 714} 715 716static bool getLiteralInfo(SourceRange literalRange, 717 bool isFloat, bool isIntZero, 718 ASTContext &Ctx, LiteralInfo &Info) { 719 if (literalRange.getBegin().isMacroID() || 720 literalRange.getEnd().isMacroID()) 721 return false; 722 StringRef text = Lexer::getSourceText( 723 CharSourceRange::getTokenRange(literalRange), 724 Ctx.getSourceManager(), Ctx.getLangOpts()); 725 if (text.empty()) 726 return false; 727 728 Optional<bool> UpperU, UpperL; 729 bool UpperF = false; 730 731 struct Suff { 732 static bool has(StringRef suff, StringRef &text) { 733 if (text.endswith(suff)) { 734 text = text.substr(0, text.size()-suff.size()); 735 return true; 736 } 737 return false; 738 } 739 }; 740 741 while (1) { 742 if (Suff::has("u", text)) { 743 UpperU = false; 744 } else if (Suff::has("U", text)) { 745 UpperU = true; 746 } else if (Suff::has("ll", text)) { 747 UpperL = false; 748 } else if (Suff::has("LL", text)) { 749 UpperL = true; 750 } else if (Suff::has("l", text)) { 751 UpperL = false; 752 } else if (Suff::has("L", text)) { 753 UpperL = true; 754 } else if (isFloat && Suff::has("f", text)) { 755 UpperF = false; 756 } else if (isFloat && Suff::has("F", text)) { 757 UpperF = true; 758 } else 759 break; 760 } 761 762 if (!UpperU.hasValue() && !UpperL.hasValue()) 763 UpperU = UpperL = true; 764 else if (UpperU.hasValue() && !UpperL.hasValue()) 765 UpperL = UpperU; 766 else if (UpperL.hasValue() && !UpperU.hasValue()) 767 UpperU = UpperL; 768 769 Info.U = *UpperU ? "U" : "u"; 770 Info.L = *UpperL ? "L" : "l"; 771 Info.LL = *UpperL ? "LL" : "ll"; 772 Info.F = UpperF ? "F" : "f"; 773 774 Info.Hex = Info.Octal = false; 775 if (text.startswith("0x")) 776 Info.Hex = true; 777 else if (!isFloat && !isIntZero && text.startswith("0")) 778 Info.Octal = true; 779 780 SourceLocation B = literalRange.getBegin(); 781 Info.WithoutSuffRange = 782 CharSourceRange::getCharRange(B, B.getLocWithOffset(text.size())); 783 return true; 784} 785 786static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, 787 const NSAPI &NS, Commit &commit) { 788 if (Msg->getNumArgs() != 1) 789 return false; 790 791 const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts(); 792 if (const CharacterLiteral *CharE = dyn_cast<CharacterLiteral>(Arg)) 793 return rewriteToCharLiteral(Msg, CharE, NS, commit); 794 if (const ObjCBoolLiteralExpr *BE = dyn_cast<ObjCBoolLiteralExpr>(Arg)) 795 return rewriteToBoolLiteral(Msg, BE, NS, commit); 796 if (const CXXBoolLiteralExpr *BE = dyn_cast<CXXBoolLiteralExpr>(Arg)) 797 return rewriteToBoolLiteral(Msg, BE, NS, commit); 798 799 const Expr *literalE = Arg; 800 if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(literalE)) { 801 if (UOE->getOpcode() == UO_Plus || UOE->getOpcode() == UO_Minus) 802 literalE = UOE->getSubExpr(); 803 } 804 805 // Only integer and floating literals, otherwise try to rewrite to boxed 806 // expression. 807 if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) 808 return rewriteToNumericBoxedExpression(Msg, NS, commit); 809 810 ASTContext &Ctx = NS.getASTContext(); 811 Selector Sel = Msg->getSelector(); 812 Optional<NSAPI::NSNumberLiteralMethodKind> 813 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 814 if (!MKOpt) 815 return false; 816 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 817 818 bool CallIsUnsigned = false, CallIsLong = false, CallIsLongLong = false; 819 bool CallIsFloating = false, CallIsDouble = false; 820 821 switch (MK) { 822 // We cannot have these calls with int/float literals. 823 case NSAPI::NSNumberWithChar: 824 case NSAPI::NSNumberWithUnsignedChar: 825 case NSAPI::NSNumberWithShort: 826 case NSAPI::NSNumberWithUnsignedShort: 827 case NSAPI::NSNumberWithBool: 828 return rewriteToNumericBoxedExpression(Msg, NS, commit); 829 830 case NSAPI::NSNumberWithUnsignedInt: 831 case NSAPI::NSNumberWithUnsignedInteger: 832 CallIsUnsigned = true; 833 case NSAPI::NSNumberWithInt: 834 case NSAPI::NSNumberWithInteger: 835 break; 836 837 case NSAPI::NSNumberWithUnsignedLong: 838 CallIsUnsigned = true; 839 case NSAPI::NSNumberWithLong: 840 CallIsLong = true; 841 break; 842 843 case NSAPI::NSNumberWithUnsignedLongLong: 844 CallIsUnsigned = true; 845 case NSAPI::NSNumberWithLongLong: 846 CallIsLongLong = true; 847 break; 848 849 case NSAPI::NSNumberWithDouble: 850 CallIsDouble = true; 851 case NSAPI::NSNumberWithFloat: 852 CallIsFloating = true; 853 break; 854 } 855 856 SourceRange ArgRange = Arg->getSourceRange(); 857 QualType ArgTy = Arg->getType(); 858 QualType CallTy = Msg->getArg(0)->getType(); 859 860 // Check for the easy case, the literal maps directly to the call. 861 if (Ctx.hasSameType(ArgTy, CallTy)) { 862 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 863 commit.insert(ArgRange.getBegin(), "@"); 864 return true; 865 } 866 867 // We will need to modify the literal suffix to get the same type as the call. 868 // Try with boxed expression if it came from a macro. 869 if (ArgRange.getBegin().isMacroID()) 870 return rewriteToNumericBoxedExpression(Msg, NS, commit); 871 872 bool LitIsFloat = ArgTy->isFloatingType(); 873 // For a float passed to integer call, don't try rewriting to objc literal. 874 // It is difficult and a very uncommon case anyway. 875 // But try with boxed expression. 876 if (LitIsFloat && !CallIsFloating) 877 return rewriteToNumericBoxedExpression(Msg, NS, commit); 878 879 // Try to modify the literal make it the same type as the method call. 880 // -Modify the suffix, and/or 881 // -Change integer to float 882 883 LiteralInfo LitInfo; 884 bool isIntZero = false; 885 if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) 886 isIntZero = !IntE->getValue().getBoolValue(); 887 if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) 888 return rewriteToNumericBoxedExpression(Msg, NS, commit); 889 890 // Not easy to do int -> float with hex/octal and uncommon anyway. 891 if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) 892 return rewriteToNumericBoxedExpression(Msg, NS, commit); 893 894 SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); 895 SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); 896 897 commit.replaceWithInner(CharSourceRange::getTokenRange(Msg->getSourceRange()), 898 LitInfo.WithoutSuffRange); 899 commit.insert(LitB, "@"); 900 901 if (!LitIsFloat && CallIsFloating) 902 commit.insert(LitE, ".0"); 903 904 if (CallIsFloating) { 905 if (!CallIsDouble) 906 commit.insert(LitE, LitInfo.F); 907 } else { 908 if (CallIsUnsigned) 909 commit.insert(LitE, LitInfo.U); 910 911 if (CallIsLong) 912 commit.insert(LitE, LitInfo.L); 913 else if (CallIsLongLong) 914 commit.insert(LitE, LitInfo.LL); 915 } 916 return true; 917} 918 919// FIXME: Make determination of operator precedence more general and 920// make it broadly available. 921static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 922 const Expr* Expr = FullExpr->IgnoreImpCasts(); 923 if (isa<ArraySubscriptExpr>(Expr) || 924 isa<CallExpr>(Expr) || 925 isa<DeclRefExpr>(Expr) || 926 isa<CXXNamedCastExpr>(Expr) || 927 isa<CXXConstructExpr>(Expr) || 928 isa<CXXThisExpr>(Expr) || 929 isa<CXXTypeidExpr>(Expr) || 930 isa<CXXUnresolvedConstructExpr>(Expr) || 931 isa<ObjCMessageExpr>(Expr) || 932 isa<ObjCPropertyRefExpr>(Expr) || 933 isa<ObjCProtocolExpr>(Expr) || 934 isa<MemberExpr>(Expr) || 935 isa<ObjCIvarRefExpr>(Expr) || 936 isa<ParenExpr>(FullExpr) || 937 isa<ParenListExpr>(Expr) || 938 isa<SizeOfPackExpr>(Expr)) 939 return false; 940 941 return true; 942} 943static bool castOperatorNeedsParens(const Expr *FullExpr) { 944 const Expr* Expr = FullExpr->IgnoreImpCasts(); 945 if (isa<ArraySubscriptExpr>(Expr) || 946 isa<CallExpr>(Expr) || 947 isa<DeclRefExpr>(Expr) || 948 isa<CastExpr>(Expr) || 949 isa<CXXNewExpr>(Expr) || 950 isa<CXXConstructExpr>(Expr) || 951 isa<CXXDeleteExpr>(Expr) || 952 isa<CXXNoexceptExpr>(Expr) || 953 isa<CXXPseudoDestructorExpr>(Expr) || 954 isa<CXXScalarValueInitExpr>(Expr) || 955 isa<CXXThisExpr>(Expr) || 956 isa<CXXTypeidExpr>(Expr) || 957 isa<CXXUnresolvedConstructExpr>(Expr) || 958 isa<ObjCMessageExpr>(Expr) || 959 isa<ObjCPropertyRefExpr>(Expr) || 960 isa<ObjCProtocolExpr>(Expr) || 961 isa<MemberExpr>(Expr) || 962 isa<ObjCIvarRefExpr>(Expr) || 963 isa<ParenExpr>(FullExpr) || 964 isa<ParenListExpr>(Expr) || 965 isa<SizeOfPackExpr>(Expr) || 966 isa<UnaryOperator>(Expr)) 967 return false; 968 969 return true; 970} 971 972static void objectifyExpr(const Expr *E, Commit &commit) { 973 if (!E) return; 974 975 QualType T = E->getType(); 976 if (T->isObjCObjectPointerType()) { 977 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { 978 if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) 979 return; 980 } else { 981 return; 982 } 983 } else if (!T->isPointerType()) { 984 return; 985 } 986 987 SourceRange Range = E->getSourceRange(); 988 if (castOperatorNeedsParens(E)) 989 commit.insertWrap("(", Range, ")"); 990 commit.insertBefore(Range.getBegin(), "(id)"); 991} 992 993//===----------------------------------------------------------------------===// 994// rewriteToNumericBoxedExpression. 995//===----------------------------------------------------------------------===// 996 997static bool isEnumConstant(const Expr *E) { 998 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) 999 if (const ValueDecl *VD = DRE->getDecl()) 1000 return isa<EnumConstantDecl>(VD); 1001 1002 return false; 1003} 1004 1005static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, 1006 const NSAPI &NS, Commit &commit) { 1007 if (Msg->getNumArgs() != 1) 1008 return false; 1009 1010 const Expr *Arg = Msg->getArg(0); 1011 if (Arg->isTypeDependent()) 1012 return false; 1013 1014 ASTContext &Ctx = NS.getASTContext(); 1015 Selector Sel = Msg->getSelector(); 1016 Optional<NSAPI::NSNumberLiteralMethodKind> 1017 MKOpt = NS.getNSNumberLiteralMethodKind(Sel); 1018 if (!MKOpt) 1019 return false; 1020 NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; 1021 1022 const Expr *OrigArg = Arg->IgnoreImpCasts(); 1023 QualType FinalTy = Arg->getType(); 1024 QualType OrigTy = OrigArg->getType(); 1025 uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); 1026 uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); 1027 1028 bool isTruncated = FinalTySize < OrigTySize; 1029 bool needsCast = false; 1030 1031 if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 1032 switch (ICE->getCastKind()) { 1033 case CK_LValueToRValue: 1034 case CK_NoOp: 1035 case CK_UserDefinedConversion: 1036 break; 1037 1038 case CK_IntegralCast: { 1039 if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) 1040 break; 1041 // Be more liberal with Integer/UnsignedInteger which are very commonly 1042 // used. 1043 if ((MK == NSAPI::NSNumberWithInteger || 1044 MK == NSAPI::NSNumberWithUnsignedInteger) && 1045 !isTruncated) { 1046 if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) 1047 break; 1048 if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && 1049 OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) 1050 break; 1051 } 1052 1053 needsCast = true; 1054 break; 1055 } 1056 1057 case CK_PointerToBoolean: 1058 case CK_IntegralToBoolean: 1059 case CK_IntegralToFloating: 1060 case CK_FloatingToIntegral: 1061 case CK_FloatingToBoolean: 1062 case CK_FloatingCast: 1063 case CK_FloatingComplexToReal: 1064 case CK_FloatingComplexToBoolean: 1065 case CK_IntegralComplexToReal: 1066 case CK_IntegralComplexToBoolean: 1067 case CK_AtomicToNonAtomic: 1068 needsCast = true; 1069 break; 1070 1071 case CK_Dependent: 1072 case CK_BitCast: 1073 case CK_LValueBitCast: 1074 case CK_BaseToDerived: 1075 case CK_DerivedToBase: 1076 case CK_UncheckedDerivedToBase: 1077 case CK_Dynamic: 1078 case CK_ToUnion: 1079 case CK_ArrayToPointerDecay: 1080 case CK_FunctionToPointerDecay: 1081 case CK_NullToPointer: 1082 case CK_NullToMemberPointer: 1083 case CK_BaseToDerivedMemberPointer: 1084 case CK_DerivedToBaseMemberPointer: 1085 case CK_MemberPointerToBoolean: 1086 case CK_ReinterpretMemberPointer: 1087 case CK_ConstructorConversion: 1088 case CK_IntegralToPointer: 1089 case CK_PointerToIntegral: 1090 case CK_ToVoid: 1091 case CK_VectorSplat: 1092 case CK_CPointerToObjCPointerCast: 1093 case CK_BlockPointerToObjCPointerCast: 1094 case CK_AnyPointerToBlockPointerCast: 1095 case CK_ObjCObjectLValueCast: 1096 case CK_FloatingRealToComplex: 1097 case CK_FloatingComplexCast: 1098 case CK_FloatingComplexToIntegralComplex: 1099 case CK_IntegralRealToComplex: 1100 case CK_IntegralComplexCast: 1101 case CK_IntegralComplexToFloatingComplex: 1102 case CK_ARCProduceObject: 1103 case CK_ARCConsumeObject: 1104 case CK_ARCReclaimReturnedObject: 1105 case CK_ARCExtendBlockObject: 1106 case CK_NonAtomicToAtomic: 1107 case CK_CopyAndAutoreleaseBlockObject: 1108 case CK_BuiltinFnToFnPtr: 1109 case CK_ZeroToOCLEvent: 1110 return false; 1111 } 1112 } 1113 1114 if (needsCast) { 1115 DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 1116 // FIXME: Use a custom category name to distinguish migration diagnostics. 1117 unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, 1118 "converting to boxing syntax requires casting %0 to %1"); 1119 Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy 1120 << Msg->getSourceRange(); 1121 return false; 1122 } 1123 1124 SourceRange ArgRange = OrigArg->getSourceRange(); 1125 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1126 1127 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1128 commit.insertBefore(ArgRange.getBegin(), "@"); 1129 else 1130 commit.insertWrap("@(", ArgRange, ")"); 1131 1132 return true; 1133} 1134 1135//===----------------------------------------------------------------------===// 1136// rewriteToStringBoxedExpression. 1137//===----------------------------------------------------------------------===// 1138 1139static bool doRewriteToUTF8StringBoxedExpressionHelper( 1140 const ObjCMessageExpr *Msg, 1141 const NSAPI &NS, Commit &commit) { 1142 const Expr *Arg = Msg->getArg(0); 1143 if (Arg->isTypeDependent()) 1144 return false; 1145 1146 ASTContext &Ctx = NS.getASTContext(); 1147 1148 const Expr *OrigArg = Arg->IgnoreImpCasts(); 1149 QualType OrigTy = OrigArg->getType(); 1150 if (OrigTy->isArrayType()) 1151 OrigTy = Ctx.getArrayDecayedType(OrigTy); 1152 1153 if (const StringLiteral * 1154 StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { 1155 commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); 1156 commit.insert(StrE->getLocStart(), "@"); 1157 return true; 1158 } 1159 1160 if (const PointerType *PT = OrigTy->getAs<PointerType>()) { 1161 QualType PointeeType = PT->getPointeeType(); 1162 if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { 1163 SourceRange ArgRange = OrigArg->getSourceRange(); 1164 commit.replaceWithInner(Msg->getSourceRange(), ArgRange); 1165 1166 if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) 1167 commit.insertBefore(ArgRange.getBegin(), "@"); 1168 else 1169 commit.insertWrap("@(", ArgRange, ")"); 1170 1171 return true; 1172 } 1173 } 1174 1175 return false; 1176} 1177 1178static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, 1179 const NSAPI &NS, Commit &commit) { 1180 Selector Sel = Msg->getSelector(); 1181 1182 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || 1183 Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { 1184 if (Msg->getNumArgs() != 1) 1185 return false; 1186 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1187 } 1188 1189 if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { 1190 if (Msg->getNumArgs() != 2) 1191 return false; 1192 1193 const Expr *encodingArg = Msg->getArg(1); 1194 if (NS.isNSUTF8StringEncodingConstant(encodingArg) || 1195 NS.isNSASCIIStringEncodingConstant(encodingArg)) 1196 return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); 1197 } 1198 1199 return false; 1200} 1201