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