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   MigrationPass &Pass;
     48   ObjCImplementationDecl *CurImplD;
     49 
     50   enum PropActionKind {
     51     PropAction_None,
     52     PropAction_RetainToStrong,
     53     PropAction_RetainRemoved,
     54     PropAction_AssignToStrong,
     55     PropAction_AssignRewritten,
     56     PropAction_MaybeAddStrong,
     57     PropAction_MaybeAddWeakOrUnsafe
     58   };
     59 
     60   struct PropData {
     61     ObjCPropertyDecl *PropD;
     62     ObjCIvarDecl *IvarD;
     63     ObjCPropertyImplDecl *ImplD;
     64 
     65     PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
     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   PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
     75 
     76   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps) {
     77     for (ObjCInterfaceDecl::prop_iterator
     78            propI = D->prop_begin(),
     79            propE = D->prop_end(); propI != propE; ++propI) {
     80       if (propI->getAtLoc().isInvalid())
     81         continue;
     82       PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
     83       props.push_back(*propI);
     84     }
     85   }
     86 
     87   void doTransform(ObjCImplementationDecl *D) {
     88     CurImplD = D;
     89     ObjCInterfaceDecl *iface = D->getClassInterface();
     90     if (!iface)
     91       return;
     92 
     93     collectProperties(iface, AtProps);
     94 
     95     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
     96         prop_impl_iterator;
     97     for (prop_impl_iterator
     98            I = prop_impl_iterator(D->decls_begin()),
     99            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
    100       ObjCPropertyImplDecl *implD = *I;
    101       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
    102         continue;
    103       ObjCPropertyDecl *propD = implD->getPropertyDecl();
    104       if (!propD || propD->isInvalidDecl())
    105         continue;
    106       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
    107       if (!ivarD || ivarD->isInvalidDecl())
    108         continue;
    109       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
    110       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
    111       if (findAtLoc == AtProps.end())
    112         continue;
    113 
    114       PropsTy &props = findAtLoc->second;
    115       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    116         if (I->PropD == propD) {
    117           I->IvarD = ivarD;
    118           I->ImplD = implD;
    119           break;
    120         }
    121       }
    122     }
    123 
    124     for (AtPropDeclsTy::iterator
    125            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
    126       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
    127       PropsTy &props = I->second;
    128       QualType ty = getPropertyType(props);
    129       if (!ty->isObjCRetainableType())
    130         continue;
    131       if (hasIvarWithExplicitOwnership(props))
    132         continue;
    133 
    134       Transaction Trans(Pass.TA);
    135       rewriteProperty(props, atLoc);
    136     }
    137 
    138     AtPropDeclsTy AtExtProps;
    139     // Look through extensions.
    140     for (ObjCCategoryDecl *Cat = iface->getCategoryList();
    141            Cat; Cat = Cat->getNextClassCategory())
    142       if (Cat->IsClassExtension())
    143         collectProperties(Cat, AtExtProps);
    144 
    145     for (AtPropDeclsTy::iterator
    146            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
    147       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
    148       PropsTy &props = I->second;
    149       Transaction Trans(Pass.TA);
    150       doActionForExtensionProp(props, atLoc);
    151     }
    152   }
    153 
    154 private:
    155   void doPropAction(PropActionKind kind,
    156                     PropsTy &props, SourceLocation atLoc,
    157                     bool markAction = true) {
    158     if (markAction)
    159       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    160         ActionOnProp[I->PropD->getIdentifier()] = kind;
    161 
    162     switch (kind) {
    163     case PropAction_None:
    164       return;
    165     case PropAction_RetainToStrong:
    166       rewriteAttribute("retain", "strong", atLoc);
    167       return;
    168     case PropAction_RetainRemoved:
    169       removeAttribute("retain", atLoc);
    170       return;
    171     case PropAction_AssignToStrong:
    172       rewriteAttribute("assign", "strong", atLoc);
    173       return;
    174     case PropAction_AssignRewritten:
    175       return rewriteAssign(props, atLoc);
    176     case PropAction_MaybeAddStrong:
    177       return maybeAddStrongAttr(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       if (propAttrs & ObjCPropertyDecl::OBJC_PR_readonly)
    203         return doPropAction(PropAction_RetainToStrong, props, atLoc);
    204       else
    205         // strong is the default.
    206         return doPropAction(PropAction_RetainRemoved, props, atLoc);
    207     }
    208 
    209     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
    210       if (hasIvarAssignedAPlusOneObject(props)) {
    211         return doPropAction(PropAction_AssignToStrong, props, atLoc);
    212       }
    213       return doPropAction(PropAction_AssignRewritten, props, atLoc);
    214     }
    215 
    216     if (hasIvarAssignedAPlusOneObject(props))
    217       return doPropAction(PropAction_MaybeAddStrong, props, atLoc);
    218 
    219     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
    220   }
    221 
    222   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
    223     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
    224 
    225     bool rewroteAttr = rewriteAttribute("assign",
    226                                      canUseWeak ? "weak" : "unsafe_unretained",
    227                                          atLoc);
    228     if (!rewroteAttr)
    229       canUseWeak = false;
    230 
    231     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    232       if (isUserDeclared(I->IvarD))
    233         Pass.TA.insert(I->IvarD->getLocation(),
    234                        canUseWeak ? "__weak " : "__unsafe_unretained ");
    235       if (I->ImplD)
    236         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
    237                                 I->ImplD->getLocation());
    238     }
    239   }
    240 
    241   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
    242                                           SourceLocation atLoc) const {
    243     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
    244 
    245     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
    246     if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
    247         !hasAllIvarsBacked(props)) {
    248       bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
    249                                     atLoc);
    250       if (!addedAttr)
    251         canUseWeak = false;
    252     }
    253 
    254     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    255       if (isUserDeclared(I->IvarD))
    256         Pass.TA.insert(I->IvarD->getLocation(),
    257                        canUseWeak ? "__weak " : "__unsafe_unretained ");
    258       if (I->ImplD) {
    259         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
    260                                 I->ImplD->getLocation());
    261         Pass.TA.clearDiagnostic(
    262                            diag::err_arc_objc_property_default_assign_on_object,
    263                            I->ImplD->getLocation());
    264       }
    265     }
    266   }
    267 
    268   void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
    269     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
    270 
    271     if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
    272         !hasAllIvarsBacked(props)) {
    273       addAttribute("strong", atLoc);
    274     }
    275 
    276     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    277       if (I->ImplD) {
    278         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
    279                                 I->ImplD->getLocation());
    280         Pass.TA.clearDiagnostic(
    281                            diag::err_arc_objc_property_default_assign_on_object,
    282                            I->ImplD->getLocation());
    283       }
    284     }
    285   }
    286 
    287   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
    288     return rewriteAttribute(fromAttr, StringRef(), atLoc);
    289   }
    290 
    291   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
    292                         SourceLocation atLoc) const {
    293     if (atLoc.isMacroID())
    294       return false;
    295 
    296     SourceManager &SM = Pass.Ctx.getSourceManager();
    297 
    298     // Break down the source location.
    299     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
    300 
    301     // Try to load the file buffer.
    302     bool invalidTemp = false;
    303     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
    304     if (invalidTemp)
    305       return false;
    306 
    307     const char *tokenBegin = file.data() + locInfo.second;
    308 
    309     // Lex from the start of the given location.
    310     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
    311                 Pass.Ctx.getLangOptions(),
    312                 file.begin(), tokenBegin, file.end());
    313     Token tok;
    314     lexer.LexFromRawLexer(tok);
    315     if (tok.isNot(tok::at)) return false;
    316     lexer.LexFromRawLexer(tok);
    317     if (tok.isNot(tok::raw_identifier)) return false;
    318     if (StringRef(tok.getRawIdentifierData(), tok.getLength())
    319           != "property")
    320       return false;
    321     lexer.LexFromRawLexer(tok);
    322     if (tok.isNot(tok::l_paren)) return false;
    323 
    324     Token BeforeTok = tok;
    325     Token AfterTok;
    326     AfterTok.startToken();
    327     SourceLocation AttrLoc;
    328 
    329     lexer.LexFromRawLexer(tok);
    330     if (tok.is(tok::r_paren))
    331       return false;
    332 
    333     while (1) {
    334       if (tok.isNot(tok::raw_identifier)) return false;
    335       StringRef ident(tok.getRawIdentifierData(), tok.getLength());
    336       if (ident == fromAttr) {
    337         if (!toAttr.empty()) {
    338           Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
    339           return true;
    340         }
    341         // We want to remove the attribute.
    342         AttrLoc = tok.getLocation();
    343       }
    344 
    345       do {
    346         lexer.LexFromRawLexer(tok);
    347         if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
    348           AfterTok = tok;
    349       } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
    350       if (tok.is(tok::r_paren))
    351         break;
    352       if (AttrLoc.isInvalid())
    353         BeforeTok = tok;
    354       lexer.LexFromRawLexer(tok);
    355     }
    356 
    357     if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
    358       // We want to remove the attribute.
    359       if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
    360         Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
    361                                    AfterTok.getLocation()));
    362       } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
    363         Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
    364       } else {
    365         Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
    366       }
    367 
    368       return true;
    369     }
    370 
    371     return false;
    372   }
    373 
    374   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
    375     if (atLoc.isMacroID())
    376       return false;
    377 
    378     SourceManager &SM = Pass.Ctx.getSourceManager();
    379 
    380     // Break down the source location.
    381     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
    382 
    383     // Try to load the file buffer.
    384     bool invalidTemp = false;
    385     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
    386     if (invalidTemp)
    387       return false;
    388 
    389     const char *tokenBegin = file.data() + locInfo.second;
    390 
    391     // Lex from the start of the given location.
    392     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
    393                 Pass.Ctx.getLangOptions(),
    394                 file.begin(), tokenBegin, file.end());
    395     Token tok;
    396     lexer.LexFromRawLexer(tok);
    397     if (tok.isNot(tok::at)) return false;
    398     lexer.LexFromRawLexer(tok);
    399     if (tok.isNot(tok::raw_identifier)) return false;
    400     if (StringRef(tok.getRawIdentifierData(), tok.getLength())
    401           != "property")
    402       return false;
    403     lexer.LexFromRawLexer(tok);
    404 
    405     if (tok.isNot(tok::l_paren)) {
    406       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
    407       return true;
    408     }
    409 
    410     lexer.LexFromRawLexer(tok);
    411     if (tok.is(tok::r_paren)) {
    412       Pass.TA.insert(tok.getLocation(), attr);
    413       return true;
    414     }
    415 
    416     if (tok.isNot(tok::raw_identifier)) return false;
    417 
    418     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
    419     return true;
    420   }
    421 
    422   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
    423     ObjCIvarDecl *Ivar;
    424   public:
    425     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
    426 
    427     bool VisitBinAssign(BinaryOperator *E) {
    428       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
    429       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
    430         if (RE->getDecl() != Ivar)
    431           return true;
    432 
    433       if (ObjCMessageExpr *
    434             ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
    435         if (ME->getMethodFamily() == OMF_retain)
    436           return false;
    437 
    438       ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
    439       while (implCE && implCE->getCastKind() ==  CK_BitCast)
    440         implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
    441 
    442       if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
    443         return false;
    444       }
    445 
    446       return true;
    447     }
    448   };
    449 
    450   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
    451     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    452       PlusOneAssign oneAssign(I->IvarD);
    453       bool notFound = oneAssign.TraverseDecl(CurImplD);
    454       if (!notFound)
    455         return true;
    456     }
    457 
    458     return false;
    459   }
    460 
    461   bool hasIvarWithExplicitOwnership(PropsTy &props) const {
    462     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    463       if (isUserDeclared(I->IvarD)) {
    464         if (isa<AttributedType>(I->IvarD->getType()))
    465           return true;
    466         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
    467               != Qualifiers::OCL_Strong)
    468           return true;
    469       }
    470     }
    471 
    472     return false;
    473   }
    474 
    475   bool hasAllIvarsBacked(PropsTy &props) const {
    476     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    477       if (!isUserDeclared(I->IvarD))
    478         return false;
    479 
    480     return true;
    481   }
    482 
    483   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
    484     return ivarD && !ivarD->getSynthesize();
    485   }
    486 
    487   QualType getPropertyType(PropsTy &props) const {
    488     assert(!props.empty());
    489     QualType ty = props[0].PropD->getType();
    490 
    491 #ifndef NDEBUG
    492     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    493       assert(ty == I->PropD->getType());
    494 #endif
    495 
    496     return ty;
    497   }
    498 
    499   ObjCPropertyDecl::PropertyAttributeKind
    500   getPropertyAttrs(PropsTy &props) const {
    501     assert(!props.empty());
    502     ObjCPropertyDecl::PropertyAttributeKind
    503       attrs = props[0].PropD->getPropertyAttributesAsWritten();
    504 
    505 #ifndef NDEBUG
    506     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    507       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
    508 #endif
    509 
    510     return attrs;
    511   }
    512 };
    513 
    514 class ImplementationChecker :
    515                              public RecursiveASTVisitor<ImplementationChecker> {
    516   MigrationPass &Pass;
    517 
    518 public:
    519   ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
    520 
    521   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
    522     PropertiesRewriter(Pass).doTransform(D);
    523     return true;
    524   }
    525 };
    526 
    527 } // anonymous namespace
    528 
    529 void trans::rewriteProperties(MigrationPass &pass) {
    530   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    531 }
    532