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