1 //===--- TransProperties.cpp - Tranformations to ARC mode -----------------===// 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 // rewriteProperties: 11 // 12 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that 13 // are missing one. 14 // - Migrates properties from (retain) to (strong) and (assign) to 15 // (unsafe_unretained/weak). 16 // - If a property is synthesized, adds the ownership specifier in the ivar 17 // backing the property. 18 // 19 // @interface Foo : NSObject { 20 // NSObject *x; 21 // } 22 // @property (assign) id x; 23 // @end 24 // ----> 25 // @interface Foo : NSObject { 26 // NSObject *__weak x; 27 // } 28 // @property (weak) id x; 29 // @end 30 // 31 //===----------------------------------------------------------------------===// 32 33 #include "Transforms.h" 34 #include "Internals.h" 35 #include "clang/Sema/SemaDiagnostic.h" 36 #include "clang/Basic/SourceManager.h" 37 #include "clang/Lex/Lexer.h" 38 #include <map> 39 40 using namespace clang; 41 using namespace arcmt; 42 using namespace trans; 43 44 namespace { 45 46 class PropertiesRewriter { 47 MigrationPass &Pass; 48 ObjCImplementationDecl *CurImplD; 49 50 enum PropActionKind { 51 PropAction_None, 52 PropAction_RetainToStrong, 53 PropAction_RetainRemoved, 54 PropAction_AssignToStrong, 55 PropAction_AssignRewritten, 56 PropAction_MaybeAddStrong, 57 PropAction_MaybeAddWeakOrUnsafe 58 }; 59 60 struct PropData { 61 ObjCPropertyDecl *PropD; 62 ObjCIvarDecl *IvarD; 63 ObjCPropertyImplDecl *ImplD; 64 65 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } 66 }; 67 68 typedef SmallVector<PropData, 2> PropsTy; 69 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 70 AtPropDeclsTy AtProps; 71 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; 72 73 public: 74 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } 75 76 static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps) { 77 for (ObjCInterfaceDecl::prop_iterator 78 propI = D->prop_begin(), 79 propE = D->prop_end(); propI != propE; ++propI) { 80 if (propI->getAtLoc().isInvalid()) 81 continue; 82 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; 83 props.push_back(*propI); 84 } 85 } 86 87 void doTransform(ObjCImplementationDecl *D) { 88 CurImplD = D; 89 ObjCInterfaceDecl *iface = D->getClassInterface(); 90 if (!iface) 91 return; 92 93 collectProperties(iface, AtProps); 94 95 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 96 prop_impl_iterator; 97 for (prop_impl_iterator 98 I = prop_impl_iterator(D->decls_begin()), 99 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 100 ObjCPropertyImplDecl *implD = *I; 101 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 102 continue; 103 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 104 if (!propD || propD->isInvalidDecl()) 105 continue; 106 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 107 if (!ivarD || ivarD->isInvalidDecl()) 108 continue; 109 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 110 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 111 if (findAtLoc == AtProps.end()) 112 continue; 113 114 PropsTy &props = findAtLoc->second; 115 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 116 if (I->PropD == propD) { 117 I->IvarD = ivarD; 118 I->ImplD = implD; 119 break; 120 } 121 } 122 } 123 124 for (AtPropDeclsTy::iterator 125 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 126 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 127 PropsTy &props = I->second; 128 QualType ty = getPropertyType(props); 129 if (!ty->isObjCRetainableType()) 130 continue; 131 if (hasIvarWithExplicitOwnership(props)) 132 continue; 133 134 Transaction Trans(Pass.TA); 135 rewriteProperty(props, atLoc); 136 } 137 138 AtPropDeclsTy AtExtProps; 139 // Look through extensions. 140 for (ObjCCategoryDecl *Cat = iface->getCategoryList(); 141 Cat; Cat = Cat->getNextClassCategory()) 142 if (Cat->IsClassExtension()) 143 collectProperties(Cat, AtExtProps); 144 145 for (AtPropDeclsTy::iterator 146 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { 147 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 148 PropsTy &props = I->second; 149 Transaction Trans(Pass.TA); 150 doActionForExtensionProp(props, atLoc); 151 } 152 } 153 154 private: 155 void doPropAction(PropActionKind kind, 156 PropsTy &props, SourceLocation atLoc, 157 bool markAction = true) { 158 if (markAction) 159 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 160 ActionOnProp[I->PropD->getIdentifier()] = kind; 161 162 switch (kind) { 163 case PropAction_None: 164 return; 165 case PropAction_RetainToStrong: 166 rewriteAttribute("retain", "strong", atLoc); 167 return; 168 case PropAction_RetainRemoved: 169 removeAttribute("retain", atLoc); 170 return; 171 case PropAction_AssignToStrong: 172 rewriteAttribute("assign", "strong", atLoc); 173 return; 174 case PropAction_AssignRewritten: 175 return rewriteAssign(props, atLoc); 176 case PropAction_MaybeAddStrong: 177 return maybeAddStrongAttr(props, atLoc); 178 case PropAction_MaybeAddWeakOrUnsafe: 179 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 180 } 181 } 182 183 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { 184 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; 185 I = ActionOnProp.find(props[0].PropD->getIdentifier()); 186 if (I == ActionOnProp.end()) 187 return; 188 189 doPropAction(I->second, props, atLoc, false); 190 } 191 192 void rewriteProperty(PropsTy &props, SourceLocation atLoc) { 193 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 194 195 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 196 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 197 ObjCPropertyDecl::OBJC_PR_strong | 198 ObjCPropertyDecl::OBJC_PR_weak)) 199 return; 200 201 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 202 if (propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) 203 return doPropAction(PropAction_RetainToStrong, props, atLoc); 204 else 205 // strong is the default. 206 return doPropAction(PropAction_RetainRemoved, props, atLoc); 207 } 208 209 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 210 if (hasIvarAssignedAPlusOneObject(props)) { 211 return doPropAction(PropAction_AssignToStrong, props, atLoc); 212 } 213 return doPropAction(PropAction_AssignRewritten, props, atLoc); 214 } 215 216 if (hasIvarAssignedAPlusOneObject(props)) 217 return doPropAction(PropAction_MaybeAddStrong, props, atLoc); 218 219 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); 220 } 221 222 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 223 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 224 225 bool rewroteAttr = rewriteAttribute("assign", 226 canUseWeak ? "weak" : "unsafe_unretained", 227 atLoc); 228 if (!rewroteAttr) 229 canUseWeak = false; 230 231 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 232 if (isUserDeclared(I->IvarD)) 233 Pass.TA.insert(I->IvarD->getLocation(), 234 canUseWeak ? "__weak " : "__unsafe_unretained "); 235 if (I->ImplD) 236 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 237 I->ImplD->getLocation()); 238 } 239 } 240 241 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 242 SourceLocation atLoc) const { 243 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 244 245 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 246 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 247 !hasAllIvarsBacked(props)) { 248 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 249 atLoc); 250 if (!addedAttr) 251 canUseWeak = false; 252 } 253 254 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 255 if (isUserDeclared(I->IvarD)) 256 Pass.TA.insert(I->IvarD->getLocation(), 257 canUseWeak ? "__weak " : "__unsafe_unretained "); 258 if (I->ImplD) { 259 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 260 I->ImplD->getLocation()); 261 Pass.TA.clearDiagnostic( 262 diag::err_arc_objc_property_default_assign_on_object, 263 I->ImplD->getLocation()); 264 } 265 } 266 } 267 268 void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const { 269 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 270 271 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 272 !hasAllIvarsBacked(props)) { 273 addAttribute("strong", atLoc); 274 } 275 276 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 277 if (I->ImplD) { 278 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 279 I->ImplD->getLocation()); 280 Pass.TA.clearDiagnostic( 281 diag::err_arc_objc_property_default_assign_on_object, 282 I->ImplD->getLocation()); 283 } 284 } 285 } 286 287 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 288 return rewriteAttribute(fromAttr, StringRef(), atLoc); 289 } 290 291 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 292 SourceLocation atLoc) const { 293 if (atLoc.isMacroID()) 294 return false; 295 296 SourceManager &SM = Pass.Ctx.getSourceManager(); 297 298 // Break down the source location. 299 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 300 301 // Try to load the file buffer. 302 bool invalidTemp = false; 303 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 304 if (invalidTemp) 305 return false; 306 307 const char *tokenBegin = file.data() + locInfo.second; 308 309 // Lex from the start of the given location. 310 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 311 Pass.Ctx.getLangOptions(), 312 file.begin(), tokenBegin, file.end()); 313 Token tok; 314 lexer.LexFromRawLexer(tok); 315 if (tok.isNot(tok::at)) return false; 316 lexer.LexFromRawLexer(tok); 317 if (tok.isNot(tok::raw_identifier)) return false; 318 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 319 != "property") 320 return false; 321 lexer.LexFromRawLexer(tok); 322 if (tok.isNot(tok::l_paren)) return false; 323 324 Token BeforeTok = tok; 325 Token AfterTok; 326 AfterTok.startToken(); 327 SourceLocation AttrLoc; 328 329 lexer.LexFromRawLexer(tok); 330 if (tok.is(tok::r_paren)) 331 return false; 332 333 while (1) { 334 if (tok.isNot(tok::raw_identifier)) return false; 335 StringRef ident(tok.getRawIdentifierData(), tok.getLength()); 336 if (ident == fromAttr) { 337 if (!toAttr.empty()) { 338 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); 339 return true; 340 } 341 // We want to remove the attribute. 342 AttrLoc = tok.getLocation(); 343 } 344 345 do { 346 lexer.LexFromRawLexer(tok); 347 if (AttrLoc.isValid() && AfterTok.is(tok::unknown)) 348 AfterTok = tok; 349 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); 350 if (tok.is(tok::r_paren)) 351 break; 352 if (AttrLoc.isInvalid()) 353 BeforeTok = tok; 354 lexer.LexFromRawLexer(tok); 355 } 356 357 if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) { 358 // We want to remove the attribute. 359 if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) { 360 Pass.TA.remove(SourceRange(BeforeTok.getLocation(), 361 AfterTok.getLocation())); 362 } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) { 363 Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation())); 364 } else { 365 Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc)); 366 } 367 368 return true; 369 } 370 371 return false; 372 } 373 374 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 375 if (atLoc.isMacroID()) 376 return false; 377 378 SourceManager &SM = Pass.Ctx.getSourceManager(); 379 380 // Break down the source location. 381 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 382 383 // Try to load the file buffer. 384 bool invalidTemp = false; 385 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 386 if (invalidTemp) 387 return false; 388 389 const char *tokenBegin = file.data() + locInfo.second; 390 391 // Lex from the start of the given location. 392 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 393 Pass.Ctx.getLangOptions(), 394 file.begin(), tokenBegin, file.end()); 395 Token tok; 396 lexer.LexFromRawLexer(tok); 397 if (tok.isNot(tok::at)) return false; 398 lexer.LexFromRawLexer(tok); 399 if (tok.isNot(tok::raw_identifier)) return false; 400 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 401 != "property") 402 return false; 403 lexer.LexFromRawLexer(tok); 404 405 if (tok.isNot(tok::l_paren)) { 406 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); 407 return true; 408 } 409 410 lexer.LexFromRawLexer(tok); 411 if (tok.is(tok::r_paren)) { 412 Pass.TA.insert(tok.getLocation(), attr); 413 return true; 414 } 415 416 if (tok.isNot(tok::raw_identifier)) return false; 417 418 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); 419 return true; 420 } 421 422 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 423 ObjCIvarDecl *Ivar; 424 public: 425 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 426 427 bool VisitBinAssign(BinaryOperator *E) { 428 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 429 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 430 if (RE->getDecl() != Ivar) 431 return true; 432 433 if (ObjCMessageExpr * 434 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) 435 if (ME->getMethodFamily() == OMF_retain) 436 return false; 437 438 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); 439 while (implCE && implCE->getCastKind() == CK_BitCast) 440 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); 441 442 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) 443 return false; 444 } 445 446 return true; 447 } 448 }; 449 450 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 451 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 452 PlusOneAssign oneAssign(I->IvarD); 453 bool notFound = oneAssign.TraverseDecl(CurImplD); 454 if (!notFound) 455 return true; 456 } 457 458 return false; 459 } 460 461 bool hasIvarWithExplicitOwnership(PropsTy &props) const { 462 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 463 if (isUserDeclared(I->IvarD)) { 464 if (isa<AttributedType>(I->IvarD->getType())) 465 return true; 466 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 467 != Qualifiers::OCL_Strong) 468 return true; 469 } 470 } 471 472 return false; 473 } 474 475 bool hasAllIvarsBacked(PropsTy &props) const { 476 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 477 if (!isUserDeclared(I->IvarD)) 478 return false; 479 480 return true; 481 } 482 483 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 484 return ivarD && !ivarD->getSynthesize(); 485 } 486 487 QualType getPropertyType(PropsTy &props) const { 488 assert(!props.empty()); 489 QualType ty = props[0].PropD->getType(); 490 491 #ifndef NDEBUG 492 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 493 assert(ty == I->PropD->getType()); 494 #endif 495 496 return ty; 497 } 498 499 ObjCPropertyDecl::PropertyAttributeKind 500 getPropertyAttrs(PropsTy &props) const { 501 assert(!props.empty()); 502 ObjCPropertyDecl::PropertyAttributeKind 503 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 504 505 #ifndef NDEBUG 506 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 507 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 508 #endif 509 510 return attrs; 511 } 512 }; 513 514 class ImplementationChecker : 515 public RecursiveASTVisitor<ImplementationChecker> { 516 MigrationPass &Pass; 517 518 public: 519 ImplementationChecker(MigrationPass &pass) : Pass(pass) { } 520 521 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { 522 PropertiesRewriter(Pass).doTransform(D); 523 return true; 524 } 525 }; 526 527 } // anonymous namespace 528 529 void trans::rewriteProperties(MigrationPass &pass) { 530 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 531 } 532