1 //===--- TransProperties.cpp - Transformations 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/Basic/SourceManager.h" 36 #include "clang/Lex/Lexer.h" 37 #include "clang/Sema/SemaDiagnostic.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_RetainReplacedWithStrong, 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 AtPropDeclsTy *PrevAtProps = 0) { 78 for (ObjCInterfaceDecl::prop_iterator 79 propI = D->prop_begin(), 80 propE = D->prop_end(); propI != propE; ++propI) { 81 if (propI->getAtLoc().isInvalid()) 82 continue; 83 unsigned RawLoc = propI->getAtLoc().getRawEncoding(); 84 if (PrevAtProps) 85 if (PrevAtProps->find(RawLoc) != PrevAtProps->end()) 86 continue; 87 PropsTy &props = AtProps[RawLoc]; 88 props.push_back(*propI); 89 } 90 } 91 92 void doTransform(ObjCImplementationDecl *D) { 93 CurImplD = D; 94 ObjCInterfaceDecl *iface = D->getClassInterface(); 95 if (!iface) 96 return; 97 98 collectProperties(iface, AtProps); 99 100 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 101 prop_impl_iterator; 102 for (prop_impl_iterator 103 I = prop_impl_iterator(D->decls_begin()), 104 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 105 ObjCPropertyImplDecl *implD = *I; 106 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 107 continue; 108 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 109 if (!propD || propD->isInvalidDecl()) 110 continue; 111 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 112 if (!ivarD || ivarD->isInvalidDecl()) 113 continue; 114 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 115 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 116 if (findAtLoc == AtProps.end()) 117 continue; 118 119 PropsTy &props = findAtLoc->second; 120 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 121 if (I->PropD == propD) { 122 I->IvarD = ivarD; 123 I->ImplD = implD; 124 break; 125 } 126 } 127 } 128 129 for (AtPropDeclsTy::iterator 130 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 131 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 132 PropsTy &props = I->second; 133 if (!getPropertyType(props)->isObjCRetainableType()) 134 continue; 135 if (hasIvarWithExplicitARCOwnership(props)) 136 continue; 137 138 Transaction Trans(Pass.TA); 139 rewriteProperty(props, atLoc); 140 } 141 142 AtPropDeclsTy AtExtProps; 143 // Look through extensions. 144 for (ObjCInterfaceDecl::visible_extensions_iterator 145 ext = iface->visible_extensions_begin(), 146 extEnd = iface->visible_extensions_end(); 147 ext != extEnd; ++ext) { 148 collectProperties(*ext, AtExtProps, &AtProps); 149 } 150 151 for (AtPropDeclsTy::iterator 152 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { 153 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 154 PropsTy &props = I->second; 155 Transaction Trans(Pass.TA); 156 doActionForExtensionProp(props, atLoc); 157 } 158 } 159 160 private: 161 void doPropAction(PropActionKind kind, 162 PropsTy &props, SourceLocation atLoc, 163 bool markAction = true) { 164 if (markAction) 165 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 166 ActionOnProp[I->PropD->getIdentifier()] = kind; 167 168 switch (kind) { 169 case PropAction_None: 170 return; 171 case PropAction_RetainReplacedWithStrong: { 172 StringRef toAttr = "strong"; 173 MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc); 174 return; 175 } 176 case PropAction_AssignRemoved: 177 return removeAssignForDefaultStrong(props, atLoc); 178 case PropAction_AssignRewritten: 179 return rewriteAssign(props, atLoc); 180 case PropAction_MaybeAddWeakOrUnsafe: 181 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 182 } 183 } 184 185 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { 186 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; 187 I = ActionOnProp.find(props[0].PropD->getIdentifier()); 188 if (I == ActionOnProp.end()) 189 return; 190 191 doPropAction(I->second, props, atLoc, false); 192 } 193 194 void rewriteProperty(PropsTy &props, SourceLocation atLoc) { 195 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 196 197 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 198 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 199 ObjCPropertyDecl::OBJC_PR_strong | 200 ObjCPropertyDecl::OBJC_PR_weak)) 201 return; 202 203 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 204 // strong is the default. 205 return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc); 206 } 207 208 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); 209 210 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 211 if (HasIvarAssignedAPlusOneObject) 212 return doPropAction(PropAction_AssignRemoved, props, atLoc); 213 return doPropAction(PropAction_AssignRewritten, props, atLoc); 214 } 215 216 if (HasIvarAssignedAPlusOneObject || 217 (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) 218 return; // 'strong' by default. 219 220 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); 221 } 222 223 void removeAssignForDefaultStrong(PropsTy &props, 224 SourceLocation atLoc) const { 225 removeAttribute("retain", atLoc); 226 if (!removeAttribute("assign", atLoc)) 227 return; 228 229 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 230 if (I->ImplD) 231 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 232 diag::err_arc_assign_property_ownership, 233 diag::err_arc_inconsistent_property_ownership, 234 I->IvarD->getLocation()); 235 } 236 } 237 238 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 239 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 240 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 241 const char *toWhich = 242 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" : 243 (canUseWeak ? "weak" : "unsafe_unretained"); 244 245 bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc); 246 if (!rewroteAttr) 247 canUseWeak = false; 248 249 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 250 if (isUserDeclared(I->IvarD)) { 251 if (I->IvarD && 252 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) { 253 const char *toWhich = 254 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " : 255 (canUseWeak ? "__weak " : "__unsafe_unretained "); 256 Pass.TA.insert(I->IvarD->getLocation(), toWhich); 257 } 258 } 259 if (I->ImplD) 260 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 261 diag::err_arc_assign_property_ownership, 262 diag::err_arc_inconsistent_property_ownership, 263 I->IvarD->getLocation()); 264 } 265 } 266 267 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 268 SourceLocation atLoc) const { 269 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 270 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 271 272 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 273 atLoc); 274 if (!addedAttr) 275 canUseWeak = false; 276 277 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 278 if (isUserDeclared(I->IvarD)) { 279 if (I->IvarD && 280 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) 281 Pass.TA.insert(I->IvarD->getLocation(), 282 canUseWeak ? "__weak " : "__unsafe_unretained "); 283 } 284 if (I->ImplD) { 285 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 286 diag::err_arc_assign_property_ownership, 287 diag::err_arc_inconsistent_property_ownership, 288 I->IvarD->getLocation()); 289 Pass.TA.clearDiagnostic( 290 diag::err_arc_objc_property_default_assign_on_object, 291 I->ImplD->getLocation()); 292 } 293 } 294 } 295 296 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 297 return MigrateCtx.removePropertyAttribute(fromAttr, atLoc); 298 } 299 300 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 301 SourceLocation atLoc) const { 302 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); 303 } 304 305 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 306 return MigrateCtx.addPropertyAttribute(attr, atLoc); 307 } 308 309 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 310 ObjCIvarDecl *Ivar; 311 public: 312 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 313 314 bool VisitBinAssign(BinaryOperator *E) { 315 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 316 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 317 if (RE->getDecl() != Ivar) 318 return true; 319 320 if (isPlusOneAssign(E)) 321 return false; 322 } 323 324 return true; 325 } 326 }; 327 328 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 329 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 330 PlusOneAssign oneAssign(I->IvarD); 331 bool notFound = oneAssign.TraverseDecl(CurImplD); 332 if (!notFound) 333 return true; 334 } 335 336 return false; 337 } 338 339 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { 340 if (Pass.isGCMigration()) 341 return false; 342 343 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 344 if (isUserDeclared(I->IvarD)) { 345 if (isa<AttributedType>(I->IvarD->getType())) 346 return true; 347 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 348 != Qualifiers::OCL_Strong) 349 return true; 350 } 351 } 352 353 return false; 354 } 355 356 bool hasAllIvarsBacked(PropsTy &props) const { 357 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 358 if (!isUserDeclared(I->IvarD)) 359 return false; 360 361 return true; 362 } 363 364 // \brief Returns true if all declarations in the @property have GC __weak. 365 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { 366 if (!Pass.isGCMigration()) 367 return false; 368 if (props.empty()) 369 return false; 370 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); 371 } 372 373 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 374 return ivarD && !ivarD->getSynthesize(); 375 } 376 377 QualType getPropertyType(PropsTy &props) const { 378 assert(!props.empty()); 379 QualType ty = props[0].PropD->getType().getUnqualifiedType(); 380 381 #ifndef NDEBUG 382 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 383 assert(ty == I->PropD->getType().getUnqualifiedType()); 384 #endif 385 386 return ty; 387 } 388 389 ObjCPropertyDecl::PropertyAttributeKind 390 getPropertyAttrs(PropsTy &props) const { 391 assert(!props.empty()); 392 ObjCPropertyDecl::PropertyAttributeKind 393 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 394 395 #ifndef NDEBUG 396 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 397 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 398 #endif 399 400 return attrs; 401 } 402 }; 403 404 } // anonymous namespace 405 406 void PropertyRewriteTraverser::traverseObjCImplementation( 407 ObjCImplementationContext &ImplCtx) { 408 PropertiesRewriter(ImplCtx.getMigrationContext()) 409 .doTransform(ImplCtx.getImplementationDecl()); 410 } 411