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) : 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 (ObjCInterfaceDecl::visible_extensions_iterator
    145            ext = iface->visible_extensions_begin(),
    146            extEnd = iface->visible_extensions_end();
    147          ext != extEnd; ++ext) {
    148       collectProperties(*ext, AtExtProps, &AtProps);
    149     }
    150 
    151     for (AtPropDeclsTy::iterator
    152            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
    153       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
    154       PropsTy &props = I->second;
    155       Transaction Trans(Pass.TA);
    156       doActionForExtensionProp(props, atLoc);
    157     }
    158   }
    159 
    160 private:
    161   void doPropAction(PropActionKind kind,
    162                     PropsTy &props, SourceLocation atLoc,
    163                     bool markAction = true) {
    164     if (markAction)
    165       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    166         ActionOnProp[I->PropD->getIdentifier()] = kind;
    167 
    168     switch (kind) {
    169     case PropAction_None:
    170       return;
    171     case PropAction_RetainReplacedWithStrong: {
    172       StringRef toAttr = "strong";
    173       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
    174       return;
    175     }
    176     case PropAction_AssignRemoved:
    177       return removeAssignForDefaultStrong(props, atLoc);
    178     case PropAction_AssignRewritten:
    179       return rewriteAssign(props, atLoc);
    180     case PropAction_MaybeAddWeakOrUnsafe:
    181       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
    182     }
    183   }
    184 
    185   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
    186     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
    187     I = ActionOnProp.find(props[0].PropD->getIdentifier());
    188     if (I == ActionOnProp.end())
    189       return;
    190 
    191     doPropAction(I->second, props, atLoc, false);
    192   }
    193 
    194   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
    195     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
    196 
    197     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
    198                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
    199                      ObjCPropertyDecl::OBJC_PR_strong |
    200                      ObjCPropertyDecl::OBJC_PR_weak))
    201       return;
    202 
    203     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
    204       // strong is the default.
    205       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
    206     }
    207 
    208     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
    209 
    210     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
    211       if (HasIvarAssignedAPlusOneObject)
    212         return doPropAction(PropAction_AssignRemoved, props, atLoc);
    213       return doPropAction(PropAction_AssignRewritten, props, atLoc);
    214     }
    215 
    216     if (HasIvarAssignedAPlusOneObject ||
    217         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
    218       return; // 'strong' by default.
    219 
    220     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
    221   }
    222 
    223   void removeAssignForDefaultStrong(PropsTy &props,
    224                                     SourceLocation atLoc) const {
    225     removeAttribute("retain", atLoc);
    226     if (!removeAttribute("assign", atLoc))
    227       return;
    228 
    229     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    230       if (I->ImplD)
    231         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
    232                                 diag::err_arc_assign_property_ownership,
    233                                 diag::err_arc_inconsistent_property_ownership,
    234                                 I->IvarD->getLocation());
    235     }
    236   }
    237 
    238   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
    239     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
    240                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
    241     const char *toWhich =
    242       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
    243       (canUseWeak ? "weak" : "unsafe_unretained");
    244 
    245     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
    246     if (!rewroteAttr)
    247       canUseWeak = false;
    248 
    249     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    250       if (isUserDeclared(I->IvarD)) {
    251         if (I->IvarD &&
    252             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
    253           const char *toWhich =
    254             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
    255               (canUseWeak ? "__weak " : "__unsafe_unretained ");
    256           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
    257         }
    258       }
    259       if (I->ImplD)
    260         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
    261                                 diag::err_arc_assign_property_ownership,
    262                                 diag::err_arc_inconsistent_property_ownership,
    263                                 I->IvarD->getLocation());
    264     }
    265   }
    266 
    267   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
    268                                           SourceLocation atLoc) const {
    269     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
    270                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
    271 
    272     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
    273                                   atLoc);
    274     if (!addedAttr)
    275       canUseWeak = false;
    276 
    277     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    278       if (isUserDeclared(I->IvarD)) {
    279         if (I->IvarD &&
    280             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
    281           Pass.TA.insert(I->IvarD->getLocation(),
    282                          canUseWeak ? "__weak " : "__unsafe_unretained ");
    283       }
    284       if (I->ImplD) {
    285         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
    286                                 diag::err_arc_assign_property_ownership,
    287                                 diag::err_arc_inconsistent_property_ownership,
    288                                 I->IvarD->getLocation());
    289         Pass.TA.clearDiagnostic(
    290                            diag::err_arc_objc_property_default_assign_on_object,
    291                            I->ImplD->getLocation());
    292       }
    293     }
    294   }
    295 
    296   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
    297     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
    298   }
    299 
    300   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
    301                         SourceLocation atLoc) const {
    302     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
    303   }
    304 
    305   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
    306     return MigrateCtx.addPropertyAttribute(attr, atLoc);
    307   }
    308 
    309   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
    310     ObjCIvarDecl *Ivar;
    311   public:
    312     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
    313 
    314     bool VisitBinAssign(BinaryOperator *E) {
    315       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
    316       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
    317         if (RE->getDecl() != Ivar)
    318           return true;
    319 
    320         if (isPlusOneAssign(E))
    321           return false;
    322       }
    323 
    324       return true;
    325     }
    326   };
    327 
    328   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
    329     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    330       PlusOneAssign oneAssign(I->IvarD);
    331       bool notFound = oneAssign.TraverseDecl(CurImplD);
    332       if (!notFound)
    333         return true;
    334     }
    335 
    336     return false;
    337   }
    338 
    339   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
    340     if (Pass.isGCMigration())
    341       return false;
    342 
    343     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    344       if (isUserDeclared(I->IvarD)) {
    345         if (isa<AttributedType>(I->IvarD->getType()))
    346           return true;
    347         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
    348               != Qualifiers::OCL_Strong)
    349           return true;
    350       }
    351     }
    352 
    353     return false;
    354   }
    355 
    356   bool hasAllIvarsBacked(PropsTy &props) const {
    357     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    358       if (!isUserDeclared(I->IvarD))
    359         return false;
    360 
    361     return true;
    362   }
    363 
    364   // \brief Returns true if all declarations in the @property have GC __weak.
    365   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
    366     if (!Pass.isGCMigration())
    367       return false;
    368     if (props.empty())
    369       return false;
    370     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
    371   }
    372 
    373   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
    374     return ivarD && !ivarD->getSynthesize();
    375   }
    376 
    377   QualType getPropertyType(PropsTy &props) const {
    378     assert(!props.empty());
    379     QualType ty = props[0].PropD->getType().getUnqualifiedType();
    380 
    381 #ifndef NDEBUG
    382     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    383       assert(ty == I->PropD->getType().getUnqualifiedType());
    384 #endif
    385 
    386     return ty;
    387   }
    388 
    389   ObjCPropertyDecl::PropertyAttributeKind
    390   getPropertyAttrs(PropsTy &props) const {
    391     assert(!props.empty());
    392     ObjCPropertyDecl::PropertyAttributeKind
    393       attrs = props[0].PropD->getPropertyAttributesAsWritten();
    394 
    395 #ifndef NDEBUG
    396     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    397       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
    398 #endif
    399 
    400     return attrs;
    401   }
    402 };
    403 
    404 } // anonymous namespace
    405 
    406 void PropertyRewriteTraverser::traverseObjCImplementation(
    407                                            ObjCImplementationContext &ImplCtx) {
    408   PropertiesRewriter(ImplCtx.getMigrationContext())
    409                                   .doTransform(ImplCtx.getImplementationDecl());
    410 }
    411