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 using llvm::StringRef;
     44 
     45 namespace {
     46 
     47 class PropertiesRewriter {
     48   MigrationPass &Pass;
     49 
     50   struct PropData {
     51     ObjCPropertyDecl *PropD;
     52     ObjCIvarDecl *IvarD;
     53     ObjCPropertyImplDecl *ImplD;
     54 
     55     PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
     56   };
     57 
     58   typedef llvm::SmallVector<PropData, 2> PropsTy;
     59   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
     60   AtPropDeclsTy AtProps;
     61 
     62 public:
     63   PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
     64 
     65   void doTransform(ObjCImplementationDecl *D) {
     66     ObjCInterfaceDecl *iface = D->getClassInterface();
     67     if (!iface)
     68       return;
     69 
     70     for (ObjCInterfaceDecl::prop_iterator
     71            propI = iface->prop_begin(),
     72            propE = iface->prop_end(); propI != propE; ++propI) {
     73       if (propI->getAtLoc().isInvalid())
     74         continue;
     75       PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
     76       props.push_back(*propI);
     77     }
     78 
     79     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
     80         prop_impl_iterator;
     81     for (prop_impl_iterator
     82            I = prop_impl_iterator(D->decls_begin()),
     83            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
     84       ObjCPropertyImplDecl *implD = *I;
     85       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
     86         continue;
     87       ObjCPropertyDecl *propD = implD->getPropertyDecl();
     88       if (!propD || propD->isInvalidDecl())
     89         continue;
     90       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
     91       if (!ivarD || ivarD->isInvalidDecl())
     92         continue;
     93       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
     94       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
     95       if (findAtLoc == AtProps.end())
     96         continue;
     97 
     98       PropsTy &props = findAtLoc->second;
     99       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    100         if (I->PropD == propD) {
    101           I->IvarD = ivarD;
    102           I->ImplD = implD;
    103           break;
    104         }
    105       }
    106     }
    107 
    108     for (AtPropDeclsTy::iterator
    109            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
    110       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
    111       PropsTy &props = I->second;
    112       QualType ty = getPropertyType(props);
    113       if (!ty->isObjCRetainableType())
    114         continue;
    115       if (hasIvarWithExplicitOwnership(props))
    116         continue;
    117 
    118       Transaction Trans(Pass.TA);
    119       rewriteProperty(props, atLoc);
    120     }
    121   }
    122 
    123 private:
    124   void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
    125     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
    126 
    127     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
    128                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
    129                      ObjCPropertyDecl::OBJC_PR_strong |
    130                      ObjCPropertyDecl::OBJC_PR_weak))
    131       return;
    132 
    133     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
    134       rewriteAttribute("retain", "strong", atLoc);
    135       return;
    136     }
    137 
    138     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
    139       return rewriteAssign(props, atLoc);
    140 
    141     return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
    142   }
    143 
    144   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
    145     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
    146 
    147     bool rewroteAttr = rewriteAttribute("assign",
    148                                      canUseWeak ? "weak" : "unsafe_unretained",
    149                                          atLoc);
    150     if (!rewroteAttr)
    151       canUseWeak = false;
    152 
    153     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    154       if (isUserDeclared(I->IvarD))
    155         Pass.TA.insert(I->IvarD->getLocation(),
    156                        canUseWeak ? "__weak " : "__unsafe_unretained ");
    157       if (I->ImplD)
    158         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
    159                                 I->ImplD->getLocation());
    160     }
    161   }
    162 
    163   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
    164                                           SourceLocation atLoc) const {
    165     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
    166     if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
    167         hasNoBackingIvars(props))
    168       return;
    169 
    170     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
    171     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
    172                                   atLoc);
    173     if (!addedAttr)
    174       canUseWeak = false;
    175 
    176     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    177       if (isUserDeclared(I->IvarD))
    178         Pass.TA.insert(I->IvarD->getLocation(),
    179                        canUseWeak ? "__weak " : "__unsafe_unretained ");
    180       if (I->ImplD) {
    181         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
    182                                 I->ImplD->getLocation());
    183         Pass.TA.clearDiagnostic(
    184                            diag::err_arc_objc_property_default_assign_on_object,
    185                            I->ImplD->getLocation());
    186       }
    187     }
    188   }
    189 
    190   bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr,
    191                         SourceLocation atLoc) const {
    192     if (atLoc.isMacroID())
    193       return false;
    194 
    195     SourceManager &SM = Pass.Ctx.getSourceManager();
    196 
    197     // Break down the source location.
    198     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
    199 
    200     // Try to load the file buffer.
    201     bool invalidTemp = false;
    202     llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
    203     if (invalidTemp)
    204       return false;
    205 
    206     const char *tokenBegin = file.data() + locInfo.second;
    207 
    208     // Lex from the start of the given location.
    209     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
    210                 Pass.Ctx.getLangOptions(),
    211                 file.begin(), tokenBegin, file.end());
    212     Token tok;
    213     lexer.LexFromRawLexer(tok);
    214     if (tok.isNot(tok::at)) return false;
    215     lexer.LexFromRawLexer(tok);
    216     if (tok.isNot(tok::raw_identifier)) return false;
    217     if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
    218           != "property")
    219       return false;
    220     lexer.LexFromRawLexer(tok);
    221     if (tok.isNot(tok::l_paren)) return false;
    222 
    223     lexer.LexFromRawLexer(tok);
    224     if (tok.is(tok::r_paren))
    225       return false;
    226 
    227     while (1) {
    228       if (tok.isNot(tok::raw_identifier)) return false;
    229       llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength());
    230       if (ident == fromAttr) {
    231         Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
    232         return true;
    233       }
    234 
    235       do {
    236         lexer.LexFromRawLexer(tok);
    237       } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
    238       if (tok.is(tok::r_paren))
    239         break;
    240       lexer.LexFromRawLexer(tok);
    241     }
    242 
    243     return false;
    244   }
    245 
    246   bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const {
    247     if (atLoc.isMacroID())
    248       return false;
    249 
    250     SourceManager &SM = Pass.Ctx.getSourceManager();
    251 
    252     // Break down the source location.
    253     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
    254 
    255     // Try to load the file buffer.
    256     bool invalidTemp = false;
    257     llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
    258     if (invalidTemp)
    259       return false;
    260 
    261     const char *tokenBegin = file.data() + locInfo.second;
    262 
    263     // Lex from the start of the given location.
    264     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
    265                 Pass.Ctx.getLangOptions(),
    266                 file.begin(), tokenBegin, file.end());
    267     Token tok;
    268     lexer.LexFromRawLexer(tok);
    269     if (tok.isNot(tok::at)) return false;
    270     lexer.LexFromRawLexer(tok);
    271     if (tok.isNot(tok::raw_identifier)) return false;
    272     if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
    273           != "property")
    274       return false;
    275     lexer.LexFromRawLexer(tok);
    276 
    277     if (tok.isNot(tok::l_paren)) {
    278       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
    279       return true;
    280     }
    281 
    282     lexer.LexFromRawLexer(tok);
    283     if (tok.is(tok::r_paren)) {
    284       Pass.TA.insert(tok.getLocation(), attr);
    285       return true;
    286     }
    287 
    288     if (tok.isNot(tok::raw_identifier)) return false;
    289 
    290     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
    291     return true;
    292   }
    293 
    294   bool hasIvarWithExplicitOwnership(PropsTy &props) const {
    295     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
    296       if (isUserDeclared(I->IvarD)) {
    297         if (isa<AttributedType>(I->IvarD->getType()))
    298           return true;
    299         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
    300               != Qualifiers::OCL_Strong)
    301           return true;
    302       }
    303     }
    304 
    305     return false;
    306   }
    307 
    308   bool hasNoBackingIvars(PropsTy &props) const {
    309     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    310       if (I->IvarD)
    311         return false;
    312 
    313     return true;
    314   }
    315 
    316   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
    317     return ivarD && !ivarD->getSynthesize();
    318   }
    319 
    320   QualType getPropertyType(PropsTy &props) const {
    321     assert(!props.empty());
    322     QualType ty = props[0].PropD->getType();
    323 
    324 #ifndef NDEBUG
    325     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    326       assert(ty == I->PropD->getType());
    327 #endif
    328 
    329     return ty;
    330   }
    331 
    332   ObjCPropertyDecl::PropertyAttributeKind
    333   getPropertyAttrs(PropsTy &props) const {
    334     assert(!props.empty());
    335     ObjCPropertyDecl::PropertyAttributeKind
    336       attrs = props[0].PropD->getPropertyAttributesAsWritten();
    337 
    338 #ifndef NDEBUG
    339     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
    340       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
    341 #endif
    342 
    343     return attrs;
    344   }
    345 };
    346 
    347 class ImplementationChecker :
    348                              public RecursiveASTVisitor<ImplementationChecker> {
    349   MigrationPass &Pass;
    350 
    351 public:
    352   ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
    353 
    354   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
    355     PropertiesRewriter(Pass).doTransform(D);
    356     return true;
    357   }
    358 };
    359 
    360 } // anonymous namespace
    361 
    362 void trans::rewriteProperties(MigrationPass &pass) {
    363   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
    364 }
    365