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