clang API Documentation
00001 //===--- TransProperties.cpp - Tranformations 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/Sema/SemaDiagnostic.h" 00036 #include "clang/Basic/SourceManager.h" 00037 #include "clang/Lex/Lexer.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) : PropD(propD), IvarD(0), ImplD(0) { } 00065 }; 00066 00067 typedef SmallVector<PropData, 2> PropsTy; 00068 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 00069 AtPropDeclsTy AtProps; 00070 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; 00071 00072 public: 00073 explicit PropertiesRewriter(MigrationContext &MigrateCtx) 00074 : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { } 00075 00076 static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps, 00077 AtPropDeclsTy *PrevAtProps = 0) { 00078 for (ObjCInterfaceDecl::prop_iterator 00079 propI = D->prop_begin(), 00080 propE = D->prop_end(); propI != propE; ++propI) { 00081 if (propI->getAtLoc().isInvalid()) 00082 continue; 00083 unsigned RawLoc = propI->getAtLoc().getRawEncoding(); 00084 if (PrevAtProps) 00085 if (PrevAtProps->find(RawLoc) != PrevAtProps->end()) 00086 continue; 00087 PropsTy &props = AtProps[RawLoc]; 00088 props.push_back(&*propI); 00089 } 00090 } 00091 00092 void doTransform(ObjCImplementationDecl *D) { 00093 CurImplD = D; 00094 ObjCInterfaceDecl *iface = D->getClassInterface(); 00095 if (!iface) 00096 return; 00097 00098 collectProperties(iface, AtProps); 00099 00100 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 00101 prop_impl_iterator; 00102 for (prop_impl_iterator 00103 I = prop_impl_iterator(D->decls_begin()), 00104 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 00105 ObjCPropertyImplDecl *implD = &*I; 00106 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 00107 continue; 00108 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 00109 if (!propD || propD->isInvalidDecl()) 00110 continue; 00111 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 00112 if (!ivarD || ivarD->isInvalidDecl()) 00113 continue; 00114 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 00115 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 00116 if (findAtLoc == AtProps.end()) 00117 continue; 00118 00119 PropsTy &props = findAtLoc->second; 00120 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00121 if (I->PropD == propD) { 00122 I->IvarD = ivarD; 00123 I->ImplD = implD; 00124 break; 00125 } 00126 } 00127 } 00128 00129 for (AtPropDeclsTy::iterator 00130 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 00131 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 00132 PropsTy &props = I->second; 00133 if (!getPropertyType(props)->isObjCRetainableType()) 00134 continue; 00135 if (hasIvarWithExplicitARCOwnership(props)) 00136 continue; 00137 00138 Transaction Trans(Pass.TA); 00139 rewriteProperty(props, atLoc); 00140 } 00141 00142 AtPropDeclsTy AtExtProps; 00143 // Look through extensions. 00144 for (ObjCCategoryDecl *Cat = iface->getCategoryList(); 00145 Cat; Cat = Cat->getNextClassCategory()) 00146 if (Cat->IsClassExtension()) 00147 collectProperties(Cat, AtExtProps, &AtProps); 00148 00149 for (AtPropDeclsTy::iterator 00150 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { 00151 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 00152 PropsTy &props = I->second; 00153 Transaction Trans(Pass.TA); 00154 doActionForExtensionProp(props, atLoc); 00155 } 00156 } 00157 00158 private: 00159 void doPropAction(PropActionKind kind, 00160 PropsTy &props, SourceLocation atLoc, 00161 bool markAction = true) { 00162 if (markAction) 00163 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00164 ActionOnProp[I->PropD->getIdentifier()] = kind; 00165 00166 switch (kind) { 00167 case PropAction_None: 00168 return; 00169 case PropAction_RetainReplacedWithStrong: { 00170 StringRef toAttr = "strong"; 00171 MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc); 00172 return; 00173 } 00174 case PropAction_AssignRemoved: 00175 return removeAssignForDefaultStrong(props, atLoc); 00176 case PropAction_AssignRewritten: 00177 return rewriteAssign(props, atLoc); 00178 case PropAction_MaybeAddWeakOrUnsafe: 00179 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 00180 } 00181 } 00182 00183 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { 00184 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; 00185 I = ActionOnProp.find(props[0].PropD->getIdentifier()); 00186 if (I == ActionOnProp.end()) 00187 return; 00188 00189 doPropAction(I->second, props, atLoc, false); 00190 } 00191 00192 void rewriteProperty(PropsTy &props, SourceLocation atLoc) { 00193 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 00194 00195 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 00196 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 00197 ObjCPropertyDecl::OBJC_PR_strong | 00198 ObjCPropertyDecl::OBJC_PR_weak)) 00199 return; 00200 00201 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 00202 // strong is the default. 00203 return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc); 00204 } 00205 00206 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); 00207 00208 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 00209 if (HasIvarAssignedAPlusOneObject) 00210 return doPropAction(PropAction_AssignRemoved, props, atLoc); 00211 return doPropAction(PropAction_AssignRewritten, props, atLoc); 00212 } 00213 00214 if (HasIvarAssignedAPlusOneObject || 00215 (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) 00216 return; // 'strong' by default. 00217 00218 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); 00219 } 00220 00221 void removeAssignForDefaultStrong(PropsTy &props, 00222 SourceLocation atLoc) const { 00223 removeAttribute("retain", atLoc); 00224 if (!removeAttribute("assign", atLoc)) 00225 return; 00226 00227 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00228 if (I->ImplD) 00229 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 00230 I->ImplD->getLocation()); 00231 } 00232 } 00233 00234 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 00235 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 00236 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 00237 const char *toWhich = 00238 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" : 00239 (canUseWeak ? "weak" : "unsafe_unretained"); 00240 00241 bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc); 00242 if (!rewroteAttr) 00243 canUseWeak = false; 00244 00245 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00246 if (isUserDeclared(I->IvarD)) { 00247 if (I->IvarD && 00248 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) { 00249 const char *toWhich = 00250 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " : 00251 (canUseWeak ? "__weak " : "__unsafe_unretained "); 00252 Pass.TA.insert(I->IvarD->getLocation(), toWhich); 00253 } 00254 } 00255 if (I->ImplD) 00256 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 00257 I->ImplD->getLocation()); 00258 } 00259 } 00260 00261 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 00262 SourceLocation atLoc) const { 00263 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), 00264 /*AllowOnUnknownClass=*/Pass.isGCMigration()); 00265 00266 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 00267 atLoc); 00268 if (!addedAttr) 00269 canUseWeak = false; 00270 00271 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00272 if (isUserDeclared(I->IvarD)) { 00273 if (I->IvarD && 00274 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) 00275 Pass.TA.insert(I->IvarD->getLocation(), 00276 canUseWeak ? "__weak " : "__unsafe_unretained "); 00277 } 00278 if (I->ImplD) { 00279 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 00280 I->ImplD->getLocation()); 00281 Pass.TA.clearDiagnostic( 00282 diag::err_arc_objc_property_default_assign_on_object, 00283 I->ImplD->getLocation()); 00284 } 00285 } 00286 } 00287 00288 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 00289 return MigrateCtx.removePropertyAttribute(fromAttr, atLoc); 00290 } 00291 00292 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 00293 SourceLocation atLoc) const { 00294 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); 00295 } 00296 00297 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 00298 return MigrateCtx.addPropertyAttribute(attr, atLoc); 00299 } 00300 00301 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 00302 ObjCIvarDecl *Ivar; 00303 public: 00304 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 00305 00306 bool VisitBinAssign(BinaryOperator *E) { 00307 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 00308 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 00309 if (RE->getDecl() != Ivar) 00310 return true; 00311 00312 if (ObjCMessageExpr * 00313 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) 00314 if (ME->getMethodFamily() == OMF_retain) 00315 return false; 00316 00317 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); 00318 while (implCE && implCE->getCastKind() == CK_BitCast) 00319 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); 00320 00321 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) 00322 return false; 00323 } 00324 00325 return true; 00326 } 00327 }; 00328 00329 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 00330 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00331 PlusOneAssign oneAssign(I->IvarD); 00332 bool notFound = oneAssign.TraverseDecl(CurImplD); 00333 if (!notFound) 00334 return true; 00335 } 00336 00337 return false; 00338 } 00339 00340 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { 00341 if (Pass.isGCMigration()) 00342 return false; 00343 00344 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 00345 if (isUserDeclared(I->IvarD)) { 00346 if (isa<AttributedType>(I->IvarD->getType())) 00347 return true; 00348 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 00349 != Qualifiers::OCL_Strong) 00350 return true; 00351 } 00352 } 00353 00354 return false; 00355 } 00356 00357 bool hasAllIvarsBacked(PropsTy &props) const { 00358 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00359 if (!isUserDeclared(I->IvarD)) 00360 return false; 00361 00362 return true; 00363 } 00364 00365 // \brief Returns true if all declarations in the @property have GC __weak. 00366 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { 00367 if (!Pass.isGCMigration()) 00368 return false; 00369 if (props.empty()) 00370 return false; 00371 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); 00372 } 00373 00374 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 00375 return ivarD && !ivarD->getSynthesize(); 00376 } 00377 00378 QualType getPropertyType(PropsTy &props) const { 00379 assert(!props.empty()); 00380 QualType ty = props[0].PropD->getType().getUnqualifiedType(); 00381 00382 #ifndef NDEBUG 00383 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00384 assert(ty == I->PropD->getType().getUnqualifiedType()); 00385 #endif 00386 00387 return ty; 00388 } 00389 00390 ObjCPropertyDecl::PropertyAttributeKind 00391 getPropertyAttrs(PropsTy &props) const { 00392 assert(!props.empty()); 00393 ObjCPropertyDecl::PropertyAttributeKind 00394 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 00395 00396 #ifndef NDEBUG 00397 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 00398 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 00399 #endif 00400 00401 return attrs; 00402 } 00403 }; 00404 00405 } // anonymous namespace 00406 00407 void PropertyRewriteTraverser::traverseObjCImplementation( 00408 ObjCImplementationContext &ImplCtx) { 00409 PropertiesRewriter(ImplCtx.getMigrationContext()) 00410 .doTransform(ImplCtx.getImplementationDecl()); 00411 }