clang API Documentation
00001 //===--- TransProperties.cpp - Transformations to ARC mode ----------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // rewriteProperties: 00011 // 00012 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that 00013 // are missing one. 00014 // - Migrates properties from (retain) to (strong) and (assign) to 00015 // (unsafe_unretained/weak). 00016 // - If a property is synthesized, adds the ownership specifier in the ivar 00017 // backing the property. 00018 // 00019 // @interface Foo : NSObject { 00020 // NSObject *x; 00021 // } 00022 // @property (assign) id x; 00023 // @end 00024 // ----> 00025 // @interface Foo : NSObject { 00026 // NSObject *__weak x; 00027 // } 00028 // @property (weak) id x; 00029 // @end 00030 // 00031 //===----------------------------------------------------------------------===// 00032 00033 #include "Transforms.h" 00034 #include "Internals.h" 00035 #include "clang/Basic/SourceManager.h" 00036 #include "clang/Lex/Lexer.h" 00037 #include "clang/Sema/SemaDiagnostic.h" 00038 #include <map> 00039 00040 using namespace clang; 00041 using namespace arcmt; 00042 using namespace trans; 00043 00044 namespace { 00045 00046 class PropertiesRewriter { 00047 MigrationContext &MigrateCtx; 00048 MigrationPass &Pass; 00049 ObjCImplementationDecl *CurImplD; 00050 00051 enum PropActionKind { 00052 PropAction_None, 00053 PropAction_RetainReplacedWithStrong, 00054 PropAction_AssignRemoved, 00055 PropAction_AssignRewritten, 00056 PropAction_MaybeAddWeakOrUnsafe 00057 }; 00058 00059 struct PropData { 00060 ObjCPropertyDecl *PropD; 00061 ObjCIvarDecl *IvarD; 00062 ObjCPropertyImplDecl *ImplD; 00063 00064 PropData(ObjCPropertyDecl *propD) 00065 : PropD(propD), IvarD(nullptr), ImplD(nullptr) {} 00066 }; 00067 00068 typedef SmallVector<PropData, 2> PropsTy; 00069 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 00070 AtPropDeclsTy AtProps; 00071 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; 00072 00073 public: 00074 explicit PropertiesRewriter(MigrationContext &MigrateCtx) 00075 : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { } 00076 00077 static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps, 00078 AtPropDeclsTy *PrevAtProps = nullptr) { 00079 for (auto *Prop : D->properties()) { 00080 if (Prop->getAtLoc().isInvalid()) 00081 continue; 00082 unsigned RawLoc = Prop->getAtLoc().getRawEncoding(); 00083 if (PrevAtProps) 00084 if (PrevAtProps->find(RawLoc) != PrevAtProps->end()) 00085 continue; 00086 PropsTy &props = AtProps[RawLoc]; 00087 props.push_back(Prop); 00088 } 00089 } 00090 00091 void doTransform(ObjCImplementationDecl *D) { 00092 CurImplD = D; 00093 ObjCInterfaceDecl *iface = D->getClassInterface(); 00094 if (!iface) 00095 return; 00096 00097 collectProperties(iface, AtProps); 00098 00099 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 00100 prop_impl_iterator; 00101 for (prop_impl_iterator 00102 I = prop_impl_iterator(D->decls_begin()), 00103 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 00104 ObjCPropertyImplDecl *implD = *I; 00105 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 00106 continue; 00107 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 00108 if (!propD || propD->isInvalidDecl()) 00109 continue; 00110 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 00111 if (!ivarD || ivarD->isInvalidDecl()) 00112 continue; 00113 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 00114 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 00115 if (findAtLoc == AtProps.end()) 00116 continue; 00117 00118 PropsTy &props = findAtLoc->second; 00119 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00120 if (I->PropD == propD) { 00121 I->IvarD = ivarD; 00122 I->ImplD = implD; 00123 break; 00124 } 00125 } 00126 } 00127 00128 for (AtPropDeclsTy::iterator 00129 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 00130 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 00131 PropsTy &props = I->second; 00132 if (!getPropertyType(props)->isObjCRetainableType()) 00133 continue; 00134 if (hasIvarWithExplicitARCOwnership(props)) 00135 continue; 00136 00137 Transaction Trans(Pass.TA); 00138 rewriteProperty(props, atLoc); 00139 } 00140 00141 AtPropDeclsTy AtExtProps; 00142 // Look through extensions. 00143 for (auto *Ext : iface->visible_extensions()) 00144 collectProperties(Ext, AtExtProps, &AtProps); 00145 00146 for (AtPropDeclsTy::iterator 00147 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { 00148 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 00149 PropsTy &props = I->second; 00150 Transaction Trans(Pass.TA); 00151 doActionForExtensionProp(props, atLoc); 00152 } 00153 } 00154 00155 private: 00156 void doPropAction(PropActionKind kind, 00157 PropsTy &props, SourceLocation atLoc, 00158 bool markAction = true) { 00159 if (markAction) 00160 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00161 ActionOnProp[I->PropD->getIdentifier()] = kind; 00162 00163 switch (kind) { 00164 case PropAction_None: 00165 return; 00166 case PropAction_RetainReplacedWithStrong: { 00167 StringRef toAttr = "strong"; 00168 MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc); 00169 return; 00170 } 00171 case PropAction_AssignRemoved: 00172 return removeAssignForDefaultStrong(props, atLoc); 00173 case PropAction_AssignRewritten: 00174 return rewriteAssign(props, atLoc); 00175 case PropAction_MaybeAddWeakOrUnsafe: 00176 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 00177 } 00178 } 00179 00180 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { 00181 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; 00182 I = ActionOnProp.find(props[0].PropD->getIdentifier()); 00183 if (I == ActionOnProp.end()) 00184 return; 00185 00186 doPropAction(I->second, props, atLoc, false); 00187 } 00188 00189 void rewriteProperty(PropsTy &props, SourceLocation atLoc) { 00190 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 00191 00192 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 00193 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 00194 ObjCPropertyDecl::OBJC_PR_strong | 00195 ObjCPropertyDecl::OBJC_PR_weak)) 00196 return; 00197 00198 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 00199 // strong is the default. 00200 return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc); 00201 } 00202 00203 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); 00204 00205 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 00206 if (HasIvarAssignedAPlusOneObject) 00207 return doPropAction(PropAction_AssignRemoved, props, atLoc); 00208 return doPropAction(PropAction_AssignRewritten, props, atLoc); 00209 } 00210 00211 if (HasIvarAssignedAPlusOneObject || 00212 (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) 00213 return; // 'strong' by default. 00214 00215 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); 00216 } 00217 00218 void removeAssignForDefaultStrong(PropsTy &props, 00219 SourceLocation atLoc) const { 00220 removeAttribute("retain", atLoc); 00221 if (!removeAttribute("assign", atLoc)) 00222 return; 00223 00224 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00225 if (I->ImplD) 00226 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 00227 diag::err_arc_assign_property_ownership, 00228 diag::err_arc_inconsistent_property_ownership, 00229 I->IvarD->getLocation()); 00230 } 00231 } 00232 00233 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 00234 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 00235 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 00236 const char *toWhich = 00237 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" : 00238 (canUseWeak ? "weak" : "unsafe_unretained"); 00239 00240 bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc); 00241 if (!rewroteAttr) 00242 canUseWeak = false; 00243 00244 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00245 if (isUserDeclared(I->IvarD)) { 00246 if (I->IvarD && 00247 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) { 00248 const char *toWhich = 00249 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " : 00250 (canUseWeak ? "__weak " : "__unsafe_unretained "); 00251 Pass.TA.insert(I->IvarD->getLocation(), toWhich); 00252 } 00253 } 00254 if (I->ImplD) 00255 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 00256 diag::err_arc_assign_property_ownership, 00257 diag::err_arc_inconsistent_property_ownership, 00258 I->IvarD->getLocation()); 00259 } 00260 } 00261 00262 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 00263 SourceLocation atLoc) const { 00264 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 00265 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 00266 00267 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 00268 atLoc); 00269 if (!addedAttr) 00270 canUseWeak = false; 00271 00272 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00273 if (isUserDeclared(I->IvarD)) { 00274 if (I->IvarD && 00275 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) 00276 Pass.TA.insert(I->IvarD->getLocation(), 00277 canUseWeak ? "__weak " : "__unsafe_unretained "); 00278 } 00279 if (I->ImplD) { 00280 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, 00281 diag::err_arc_assign_property_ownership, 00282 diag::err_arc_inconsistent_property_ownership, 00283 I->IvarD->getLocation()); 00284 Pass.TA.clearDiagnostic( 00285 diag::err_arc_objc_property_default_assign_on_object, 00286 I->ImplD->getLocation()); 00287 } 00288 } 00289 } 00290 00291 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 00292 return MigrateCtx.removePropertyAttribute(fromAttr, atLoc); 00293 } 00294 00295 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 00296 SourceLocation atLoc) const { 00297 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); 00298 } 00299 00300 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 00301 return MigrateCtx.addPropertyAttribute(attr, atLoc); 00302 } 00303 00304 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 00305 ObjCIvarDecl *Ivar; 00306 public: 00307 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 00308 00309 bool VisitBinAssign(BinaryOperator *E) { 00310 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 00311 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 00312 if (RE->getDecl() != Ivar) 00313 return true; 00314 00315 if (isPlusOneAssign(E)) 00316 return false; 00317 } 00318 00319 return true; 00320 } 00321 }; 00322 00323 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 00324 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00325 PlusOneAssign oneAssign(I->IvarD); 00326 bool notFound = oneAssign.TraverseDecl(CurImplD); 00327 if (!notFound) 00328 return true; 00329 } 00330 00331 return false; 00332 } 00333 00334 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { 00335 if (Pass.isGCMigration()) 00336 return false; 00337 00338 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00339 if (isUserDeclared(I->IvarD)) { 00340 if (isa<AttributedType>(I->IvarD->getType())) 00341 return true; 00342 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 00343 != Qualifiers::OCL_Strong) 00344 return true; 00345 } 00346 } 00347 00348 return false; 00349 } 00350 00351 // \brief Returns true if all declarations in the @property have GC __weak. 00352 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { 00353 if (!Pass.isGCMigration()) 00354 return false; 00355 if (props.empty()) 00356 return false; 00357 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); 00358 } 00359 00360 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 00361 return ivarD && !ivarD->getSynthesize(); 00362 } 00363 00364 QualType getPropertyType(PropsTy &props) const { 00365 assert(!props.empty()); 00366 QualType ty = props[0].PropD->getType().getUnqualifiedType(); 00367 00368 #ifndef NDEBUG 00369 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00370 assert(ty == I->PropD->getType().getUnqualifiedType()); 00371 #endif 00372 00373 return ty; 00374 } 00375 00376 ObjCPropertyDecl::PropertyAttributeKind 00377 getPropertyAttrs(PropsTy &props) const { 00378 assert(!props.empty()); 00379 ObjCPropertyDecl::PropertyAttributeKind 00380 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 00381 00382 #ifndef NDEBUG 00383 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00384 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 00385 #endif 00386 00387 return attrs; 00388 } 00389 }; 00390 00391 } // anonymous namespace 00392 00393 void PropertyRewriteTraverser::traverseObjCImplementation( 00394 ObjCImplementationContext &ImplCtx) { 00395 PropertiesRewriter(ImplCtx.getMigrationContext()) 00396 .doTransform(ImplCtx.getImplementationDecl()); 00397 }