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