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 MigrationContext &MigrateCtx; 48 MigrationPass &Pass; 49 ObjCImplementationDecl *CurImplD; 50 51 enum PropActionKind { 52 PropAction_None, 53 PropAction_RetainRemoved, 54 PropAction_AssignRemoved, 55 PropAction_AssignRewritten, 56 PropAction_MaybeAddWeakOrUnsafe 57 }; 58 59 struct PropData { 60 ObjCPropertyDecl *PropD; 61 ObjCIvarDecl *IvarD; 62 ObjCPropertyImplDecl *ImplD; 63 64 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } 65 }; 66 67 typedef SmallVector<PropData, 2> PropsTy; 68 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 69 AtPropDeclsTy AtProps; 70 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; 71 72 public: 73 explicit PropertiesRewriter(MigrationContext &MigrateCtx) 74 : MigrateCtx(MigrateCtx), Pass(MigrateCtx.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 if (!getPropertyType(props)->isObjCRetainableType()) 129 continue; 130 if (hasIvarWithExplicitARCOwnership(props)) 131 continue; 132 133 Transaction Trans(Pass.TA); 134 rewriteProperty(props, atLoc); 135 } 136 137 AtPropDeclsTy AtExtProps; 138 // Look through extensions. 139 for (ObjCCategoryDecl *Cat = iface->getCategoryList(); 140 Cat; Cat = Cat->getNextClassCategory()) 141 if (Cat->IsClassExtension()) 142 collectProperties(Cat, AtExtProps); 143 144 for (AtPropDeclsTy::iterator 145 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { 146 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 147 PropsTy &props = I->second; 148 Transaction Trans(Pass.TA); 149 doActionForExtensionProp(props, atLoc); 150 } 151 } 152 153 private: 154 void doPropAction(PropActionKind kind, 155 PropsTy &props, SourceLocation atLoc, 156 bool markAction = true) { 157 if (markAction) 158 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 159 ActionOnProp[I->PropD->getIdentifier()] = kind; 160 161 switch (kind) { 162 case PropAction_None: 163 return; 164 case PropAction_RetainRemoved: 165 removeAttribute("retain", atLoc); 166 return; 167 case PropAction_AssignRemoved: 168 return removeAssignForDefaultStrong(props, atLoc); 169 case PropAction_AssignRewritten: 170 return rewriteAssign(props, atLoc); 171 case PropAction_MaybeAddWeakOrUnsafe: 172 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 173 } 174 } 175 176 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { 177 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; 178 I = ActionOnProp.find(props[0].PropD->getIdentifier()); 179 if (I == ActionOnProp.end()) 180 return; 181 182 doPropAction(I->second, props, atLoc, false); 183 } 184 185 void rewriteProperty(PropsTy &props, SourceLocation atLoc) { 186 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 187 188 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 189 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 190 ObjCPropertyDecl::OBJC_PR_strong | 191 ObjCPropertyDecl::OBJC_PR_weak)) 192 return; 193 194 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 195 // strong is the default. 196 return doPropAction(PropAction_RetainRemoved, props, atLoc); 197 } 198 199 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); 200 201 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 202 if (HasIvarAssignedAPlusOneObject || 203 (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) { 204 return doPropAction(PropAction_AssignRemoved, props, atLoc); 205 } 206 return doPropAction(PropAction_AssignRewritten, props, atLoc); 207 } 208 209 if (HasIvarAssignedAPlusOneObject || 210 (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) 211 return; // 'strong' by default. 212 213 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); 214 } 215 216 void removeAssignForDefaultStrong(PropsTy &props, 217 SourceLocation atLoc) const { 218 removeAttribute("retain", atLoc); 219 if (!removeAttribute("assign", atLoc)) 220 return; 221 222 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 223 if (I->ImplD) 224 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 225 I->ImplD->getLocation()); 226 } 227 } 228 229 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 230 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 231 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 232 233 bool rewroteAttr = rewriteAttribute("assign", 234 canUseWeak ? "weak" : "unsafe_unretained", 235 atLoc); 236 if (!rewroteAttr) 237 canUseWeak = false; 238 239 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 240 if (isUserDeclared(I->IvarD)) 241 Pass.TA.insert(I->IvarD->getLocation(), 242 canUseWeak ? "__weak " : "__unsafe_unretained "); 243 if (I->ImplD) 244 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 245 I->ImplD->getLocation()); 246 } 247 } 248 249 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 250 SourceLocation atLoc) const { 251 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 252 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 253 254 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 255 atLoc); 256 if (!addedAttr) 257 canUseWeak = false; 258 259 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 260 if (isUserDeclared(I->IvarD)) 261 Pass.TA.insert(I->IvarD->getLocation(), 262 canUseWeak ? "__weak " : "__unsafe_unretained "); 263 if (I->ImplD) { 264 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 265 I->ImplD->getLocation()); 266 Pass.TA.clearDiagnostic( 267 diag::err_arc_objc_property_default_assign_on_object, 268 I->ImplD->getLocation()); 269 } 270 } 271 } 272 273 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 274 return rewriteAttribute(fromAttr, StringRef(), atLoc); 275 } 276 277 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 278 SourceLocation atLoc) const { 279 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); 280 } 281 282 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 283 if (atLoc.isMacroID()) 284 return false; 285 286 SourceManager &SM = Pass.Ctx.getSourceManager(); 287 288 // Break down the source location. 289 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 290 291 // Try to load the file buffer. 292 bool invalidTemp = false; 293 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 294 if (invalidTemp) 295 return false; 296 297 const char *tokenBegin = file.data() + locInfo.second; 298 299 // Lex from the start of the given location. 300 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 301 Pass.Ctx.getLangOptions(), 302 file.begin(), tokenBegin, file.end()); 303 Token tok; 304 lexer.LexFromRawLexer(tok); 305 if (tok.isNot(tok::at)) return false; 306 lexer.LexFromRawLexer(tok); 307 if (tok.isNot(tok::raw_identifier)) return false; 308 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 309 != "property") 310 return false; 311 lexer.LexFromRawLexer(tok); 312 313 if (tok.isNot(tok::l_paren)) { 314 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); 315 return true; 316 } 317 318 lexer.LexFromRawLexer(tok); 319 if (tok.is(tok::r_paren)) { 320 Pass.TA.insert(tok.getLocation(), attr); 321 return true; 322 } 323 324 if (tok.isNot(tok::raw_identifier)) return false; 325 326 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); 327 return true; 328 } 329 330 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 331 ObjCIvarDecl *Ivar; 332 public: 333 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 334 335 bool VisitBinAssign(BinaryOperator *E) { 336 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 337 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 338 if (RE->getDecl() != Ivar) 339 return true; 340 341 if (ObjCMessageExpr * 342 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) 343 if (ME->getMethodFamily() == OMF_retain) 344 return false; 345 346 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); 347 while (implCE && implCE->getCastKind() == CK_BitCast) 348 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); 349 350 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) 351 return false; 352 } 353 354 return true; 355 } 356 }; 357 358 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 359 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 360 PlusOneAssign oneAssign(I->IvarD); 361 bool notFound = oneAssign.TraverseDecl(CurImplD); 362 if (!notFound) 363 return true; 364 } 365 366 return false; 367 } 368 369 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { 370 if (Pass.isGCMigration()) 371 return false; 372 373 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 374 if (isUserDeclared(I->IvarD)) { 375 if (isa<AttributedType>(I->IvarD->getType())) 376 return true; 377 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 378 != Qualifiers::OCL_Strong) 379 return true; 380 } 381 } 382 383 return false; 384 } 385 386 bool hasAllIvarsBacked(PropsTy &props) const { 387 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 388 if (!isUserDeclared(I->IvarD)) 389 return false; 390 391 return true; 392 } 393 394 // \brief Returns true if all declarations in the @property have GC __weak. 395 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { 396 if (!Pass.isGCMigration()) 397 return false; 398 if (props.empty()) 399 return false; 400 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); 401 } 402 403 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 404 return ivarD && !ivarD->getSynthesize(); 405 } 406 407 QualType getPropertyType(PropsTy &props) const { 408 assert(!props.empty()); 409 QualType ty = props[0].PropD->getType().getUnqualifiedType(); 410 411 #ifndef NDEBUG 412 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 413 assert(ty == I->PropD->getType().getUnqualifiedType()); 414 #endif 415 416 return ty; 417 } 418 419 ObjCPropertyDecl::PropertyAttributeKind 420 getPropertyAttrs(PropsTy &props) const { 421 assert(!props.empty()); 422 ObjCPropertyDecl::PropertyAttributeKind 423 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 424 425 #ifndef NDEBUG 426 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 427 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 428 #endif 429 430 return attrs; 431 } 432 }; 433 434 } // anonymous namespace 435 436 void PropertyRewriteTraverser::traverseObjCImplementation( 437 ObjCImplementationContext &ImplCtx) { 438 PropertiesRewriter(ImplCtx.getMigrationContext()) 439 .doTransform(ImplCtx.getImplementationDecl()); 440 } 441