clang API Documentation

TransProperties.cpp
Go to the documentation of this file.
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 }