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