clang API Documentation

TransProperties.cpp
Go to the documentation of this file.
00001 //===--- TransProperties.cpp - Transformations 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/Basic/SourceManager.h"
00036 #include "clang/Lex/Lexer.h"
00037 #include "clang/Sema/SemaDiagnostic.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)
00065       : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
00066   };
00067 
00068   typedef SmallVector<PropData, 2> PropsTy;
00069   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
00070   AtPropDeclsTy AtProps;
00071   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
00072 
00073 public:
00074   explicit PropertiesRewriter(MigrationContext &MigrateCtx)
00075     : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
00076 
00077   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
00078                                 AtPropDeclsTy *PrevAtProps = nullptr) {
00079     for (auto *Prop : D->properties()) {
00080       if (Prop->getAtLoc().isInvalid())
00081         continue;
00082       unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
00083       if (PrevAtProps)
00084         if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
00085           continue;
00086       PropsTy &props = AtProps[RawLoc];
00087       props.push_back(Prop);
00088     }
00089   }
00090 
00091   void doTransform(ObjCImplementationDecl *D) {
00092     CurImplD = D;
00093     ObjCInterfaceDecl *iface = D->getClassInterface();
00094     if (!iface)
00095       return;
00096 
00097     collectProperties(iface, AtProps);
00098 
00099     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
00100         prop_impl_iterator;
00101     for (prop_impl_iterator
00102            I = prop_impl_iterator(D->decls_begin()),
00103            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
00104       ObjCPropertyImplDecl *implD = *I;
00105       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
00106         continue;
00107       ObjCPropertyDecl *propD = implD->getPropertyDecl();
00108       if (!propD || propD->isInvalidDecl())
00109         continue;
00110       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
00111       if (!ivarD || ivarD->isInvalidDecl())
00112         continue;
00113       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
00114       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
00115       if (findAtLoc == AtProps.end())
00116         continue;
00117       
00118       PropsTy &props = findAtLoc->second;
00119       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00120         if (I->PropD == propD) {
00121           I->IvarD = ivarD;
00122           I->ImplD = implD;
00123           break;
00124         }
00125       }
00126     }
00127 
00128     for (AtPropDeclsTy::iterator
00129            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
00130       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
00131       PropsTy &props = I->second;
00132       if (!getPropertyType(props)->isObjCRetainableType())
00133         continue;
00134       if (hasIvarWithExplicitARCOwnership(props))
00135         continue;
00136       
00137       Transaction Trans(Pass.TA);
00138       rewriteProperty(props, atLoc);
00139     }
00140 
00141     AtPropDeclsTy AtExtProps;
00142     // Look through extensions.
00143     for (auto *Ext : iface->visible_extensions())
00144       collectProperties(Ext, AtExtProps, &AtProps);
00145 
00146     for (AtPropDeclsTy::iterator
00147            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
00148       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
00149       PropsTy &props = I->second;
00150       Transaction Trans(Pass.TA);
00151       doActionForExtensionProp(props, atLoc);
00152     }
00153   }
00154 
00155 private:
00156   void doPropAction(PropActionKind kind,
00157                     PropsTy &props, SourceLocation atLoc,
00158                     bool markAction = true) {
00159     if (markAction)
00160       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
00161         ActionOnProp[I->PropD->getIdentifier()] = kind;
00162 
00163     switch (kind) {
00164     case PropAction_None:
00165       return;
00166     case PropAction_RetainReplacedWithStrong: {
00167       StringRef toAttr = "strong";
00168       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
00169       return;
00170     }
00171     case PropAction_AssignRemoved:
00172       return removeAssignForDefaultStrong(props, atLoc);
00173     case PropAction_AssignRewritten:
00174       return rewriteAssign(props, atLoc);
00175     case PropAction_MaybeAddWeakOrUnsafe:
00176       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
00177     }
00178   }
00179 
00180   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
00181     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
00182     I = ActionOnProp.find(props[0].PropD->getIdentifier());
00183     if (I == ActionOnProp.end())
00184       return;
00185 
00186     doPropAction(I->second, props, atLoc, false);
00187   }
00188 
00189   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
00190     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
00191     
00192     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
00193                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
00194                      ObjCPropertyDecl::OBJC_PR_strong |
00195                      ObjCPropertyDecl::OBJC_PR_weak))
00196       return;
00197 
00198     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
00199       // strong is the default.
00200       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
00201     }
00202 
00203     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
00204 
00205     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
00206       if (HasIvarAssignedAPlusOneObject)
00207         return doPropAction(PropAction_AssignRemoved, props, atLoc);
00208       return doPropAction(PropAction_AssignRewritten, props, atLoc);
00209     }
00210 
00211     if (HasIvarAssignedAPlusOneObject ||
00212         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
00213       return; // 'strong' by default.
00214 
00215     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
00216   }
00217 
00218   void removeAssignForDefaultStrong(PropsTy &props,
00219                                     SourceLocation atLoc) const {
00220     removeAttribute("retain", atLoc);
00221     if (!removeAttribute("assign", atLoc))
00222       return;
00223 
00224     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00225       if (I->ImplD)
00226         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
00227                                 diag::err_arc_assign_property_ownership,
00228                                 diag::err_arc_inconsistent_property_ownership,
00229                                 I->IvarD->getLocation());
00230     }
00231   }
00232 
00233   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
00234     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
00235                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
00236     const char *toWhich = 
00237       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
00238       (canUseWeak ? "weak" : "unsafe_unretained");
00239 
00240     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
00241     if (!rewroteAttr)
00242       canUseWeak = false;
00243 
00244     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00245       if (isUserDeclared(I->IvarD)) {
00246         if (I->IvarD &&
00247             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
00248           const char *toWhich = 
00249             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
00250               (canUseWeak ? "__weak " : "__unsafe_unretained ");
00251           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
00252         }
00253       }
00254       if (I->ImplD)
00255         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
00256                                 diag::err_arc_assign_property_ownership,
00257                                 diag::err_arc_inconsistent_property_ownership,
00258                                 I->IvarD->getLocation());
00259     }
00260   }
00261 
00262   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
00263                                           SourceLocation atLoc) const {
00264     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
00265                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
00266 
00267     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
00268                                   atLoc);
00269     if (!addedAttr)
00270       canUseWeak = false;
00271 
00272     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00273       if (isUserDeclared(I->IvarD)) {
00274         if (I->IvarD &&
00275             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
00276           Pass.TA.insert(I->IvarD->getLocation(),
00277                          canUseWeak ? "__weak " : "__unsafe_unretained ");
00278       }
00279       if (I->ImplD) {
00280         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
00281                                 diag::err_arc_assign_property_ownership,
00282                                 diag::err_arc_inconsistent_property_ownership,
00283                                 I->IvarD->getLocation());
00284         Pass.TA.clearDiagnostic(
00285                            diag::err_arc_objc_property_default_assign_on_object,
00286                            I->ImplD->getLocation());
00287       }
00288     }
00289   }
00290 
00291   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
00292     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
00293   }
00294 
00295   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
00296                         SourceLocation atLoc) const {
00297     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
00298   }
00299 
00300   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
00301     return MigrateCtx.addPropertyAttribute(attr, atLoc);
00302   }
00303 
00304   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
00305     ObjCIvarDecl *Ivar;
00306   public:
00307     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
00308 
00309     bool VisitBinAssign(BinaryOperator *E) {
00310       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
00311       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
00312         if (RE->getDecl() != Ivar)
00313           return true;
00314 
00315         if (isPlusOneAssign(E))
00316           return false;
00317       }
00318 
00319       return true;
00320     }
00321   };
00322 
00323   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
00324     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00325       PlusOneAssign oneAssign(I->IvarD);
00326       bool notFound = oneAssign.TraverseDecl(CurImplD);
00327       if (!notFound)
00328         return true;
00329     }
00330 
00331     return false;
00332   }
00333 
00334   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
00335     if (Pass.isGCMigration())
00336       return false;
00337 
00338     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
00339       if (isUserDeclared(I->IvarD)) {
00340         if (isa<AttributedType>(I->IvarD->getType()))
00341           return true;
00342         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
00343               != Qualifiers::OCL_Strong)
00344           return true;
00345       }
00346     }
00347 
00348     return false;    
00349   }
00350 
00351   // \brief Returns true if all declarations in the @property have GC __weak.
00352   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
00353     if (!Pass.isGCMigration())
00354       return false;
00355     if (props.empty())
00356       return false;
00357     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
00358   }
00359 
00360   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
00361     return ivarD && !ivarD->getSynthesize();
00362   }
00363 
00364   QualType getPropertyType(PropsTy &props) const {
00365     assert(!props.empty());
00366     QualType ty = props[0].PropD->getType().getUnqualifiedType();
00367 
00368 #ifndef NDEBUG
00369     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
00370       assert(ty == I->PropD->getType().getUnqualifiedType());
00371 #endif
00372 
00373     return ty;
00374   }
00375 
00376   ObjCPropertyDecl::PropertyAttributeKind
00377   getPropertyAttrs(PropsTy &props) const {
00378     assert(!props.empty());
00379     ObjCPropertyDecl::PropertyAttributeKind
00380       attrs = props[0].PropD->getPropertyAttributesAsWritten();
00381 
00382 #ifndef NDEBUG
00383     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
00384       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
00385 #endif
00386 
00387     return attrs;
00388   }
00389 };
00390 
00391 } // anonymous namespace
00392 
00393 void PropertyRewriteTraverser::traverseObjCImplementation(
00394                                            ObjCImplementationContext &ImplCtx) {
00395   PropertiesRewriter(ImplCtx.getMigrationContext())
00396                                   .doTransform(ImplCtx.getImplementationDecl());
00397 }