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