Home | History | Annotate | Download | only in ARCMigrate
      1 //===--- TransGCAttrs.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 #include "Transforms.h"
     11 #include "Internals.h"
     12 #include "clang/AST/ASTContext.h"
     13 #include "clang/Basic/SourceManager.h"
     14 #include "clang/Lex/Lexer.h"
     15 #include "clang/Sema/SemaDiagnostic.h"
     16 #include "llvm/ADT/SmallString.h"
     17 #include "llvm/ADT/TinyPtrVector.h"
     18 #include "llvm/Support/SaveAndRestore.h"
     19 
     20 using namespace clang;
     21 using namespace arcmt;
     22 using namespace trans;
     23 
     24 namespace {
     25 
     26 /// \brief Collects all the places where GC attributes __strong/__weak occur.
     27 class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
     28   MigrationContext &MigrateCtx;
     29   bool FullyMigratable;
     30   std::vector<ObjCPropertyDecl *> &AllProps;
     31 
     32   typedef RecursiveASTVisitor<GCAttrsCollector> base;
     33 public:
     34   GCAttrsCollector(MigrationContext &ctx,
     35                    std::vector<ObjCPropertyDecl *> &AllProps)
     36     : MigrateCtx(ctx), FullyMigratable(false),
     37       AllProps(AllProps) { }
     38 
     39   bool shouldWalkTypesOfTypeLocs() const { return false; }
     40 
     41   bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
     42     handleAttr(TL);
     43     return true;
     44   }
     45 
     46   bool TraverseDecl(Decl *D) {
     47     if (!D || D->isImplicit())
     48       return true;
     49 
     50     SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D));
     51 
     52     if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) {
     53       lookForAttribute(PropD, PropD->getTypeSourceInfo());
     54       AllProps.push_back(PropD);
     55     } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
     56       lookForAttribute(DD, DD->getTypeSourceInfo());
     57     }
     58     return base::TraverseDecl(D);
     59   }
     60 
     61   void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) {
     62     if (!TInfo)
     63       return;
     64     TypeLoc TL = TInfo->getTypeLoc();
     65     while (TL) {
     66       if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) {
     67         TL = QL.getUnqualifiedLoc();
     68       } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) {
     69         if (handleAttr(Attr, D))
     70           break;
     71         TL = Attr.getModifiedLoc();
     72       } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
     73         TL = Arr.getElementLoc();
     74       } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
     75         TL = PT.getPointeeLoc();
     76       } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
     77         TL = RT.getPointeeLoc();
     78       else
     79         break;
     80     }
     81   }
     82 
     83   bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
     84     if (TL.getAttrKind() != AttributedType::attr_objc_ownership)
     85       return false;
     86 
     87     SourceLocation Loc = TL.getAttrNameLoc();
     88     unsigned RawLoc = Loc.getRawEncoding();
     89     if (MigrateCtx.AttrSet.count(RawLoc))
     90       return true;
     91 
     92     ASTContext &Ctx = MigrateCtx.Pass.Ctx;
     93     SourceManager &SM = Ctx.getSourceManager();
     94     if (Loc.isMacroID())
     95       Loc = SM.getImmediateExpansionRange(Loc).first;
     96     SmallString<32> Buf;
     97     bool Invalid = false;
     98     StringRef Spell = Lexer::getSpelling(
     99                                   SM.getSpellingLoc(TL.getAttrEnumOperandLoc()),
    100                                   Buf, SM, Ctx.getLangOpts(), &Invalid);
    101     if (Invalid)
    102       return false;
    103     MigrationContext::GCAttrOccurrence::AttrKind Kind;
    104     if (Spell == "strong")
    105       Kind = MigrationContext::GCAttrOccurrence::Strong;
    106     else if (Spell == "weak")
    107       Kind = MigrationContext::GCAttrOccurrence::Weak;
    108     else
    109       return false;
    110 
    111     MigrateCtx.AttrSet.insert(RawLoc);
    112     MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
    113     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
    114 
    115     Attr.Kind = Kind;
    116     Attr.Loc = Loc;
    117     Attr.ModifiedType = TL.getModifiedLoc().getType();
    118     Attr.Dcl = D;
    119     Attr.FullyMigratable = FullyMigratable;
    120     return true;
    121   }
    122 
    123   bool isMigratable(Decl *D) {
    124     if (isa<TranslationUnitDecl>(D))
    125       return false;
    126 
    127     if (isInMainFile(D))
    128       return true;
    129 
    130     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
    131       return FD->hasBody();
    132 
    133     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
    134       return hasObjCImpl(ContD);
    135 
    136     if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
    137       for (const auto *MI : RD->methods()) {
    138         if (MI->isOutOfLine())
    139           return true;
    140       }
    141       return false;
    142     }
    143 
    144     return isMigratable(cast<Decl>(D->getDeclContext()));
    145   }
    146 
    147   static bool hasObjCImpl(Decl *D) {
    148     if (!D)
    149       return false;
    150     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
    151       if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
    152         return ID->getImplementation() != nullptr;
    153       if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
    154         return CD->getImplementation() != nullptr;
    155       if (isa<ObjCImplDecl>(ContD))
    156         return true;
    157       return false;
    158     }
    159     return false;
    160   }
    161 
    162   bool isInMainFile(Decl *D) {
    163     if (!D)
    164       return false;
    165 
    166     for (auto I : D->redecls())
    167       if (!isInMainFile(I->getLocation()))
    168         return false;
    169 
    170     return true;
    171   }
    172 
    173   bool isInMainFile(SourceLocation Loc) {
    174     if (Loc.isInvalid())
    175       return false;
    176 
    177     SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
    178     return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
    179   }
    180 };
    181 
    182 } // anonymous namespace
    183 
    184 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
    185   TransformActions &TA = MigrateCtx.Pass.TA;
    186 
    187   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
    188     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
    189     if (Attr.FullyMigratable && Attr.Dcl) {
    190       if (Attr.ModifiedType.isNull())
    191         continue;
    192       if (!Attr.ModifiedType->isObjCRetainableType()) {
    193         TA.reportError("GC managed memory will become unmanaged in ARC",
    194                        Attr.Loc);
    195       }
    196     }
    197   }
    198 }
    199 
    200 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
    201   TransformActions &TA = MigrateCtx.Pass.TA;
    202 
    203   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
    204     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
    205     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
    206       if (Attr.ModifiedType.isNull() ||
    207           !Attr.ModifiedType->isObjCRetainableType())
    208         continue;
    209       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
    210                         /*AllowOnUnknownClass=*/true)) {
    211         Transaction Trans(TA);
    212         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
    213           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
    214         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
    215                            diag::err_arc_unsupported_weak_class,
    216                            Attr.Loc);
    217       }
    218     }
    219   }
    220 }
    221 
    222 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
    223 
    224 static void checkAllAtProps(MigrationContext &MigrateCtx,
    225                             SourceLocation AtLoc,
    226                             IndivPropsTy &IndProps) {
    227   if (IndProps.empty())
    228     return;
    229 
    230   for (IndivPropsTy::iterator
    231          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
    232     QualType T = (*PI)->getType();
    233     if (T.isNull() || !T->isObjCRetainableType())
    234       return;
    235   }
    236 
    237   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
    238   bool hasWeak = false, hasStrong = false;
    239   ObjCPropertyDecl::PropertyAttributeKind
    240     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
    241   for (IndivPropsTy::iterator
    242          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
    243     ObjCPropertyDecl *PD = *PI;
    244     Attrs = PD->getPropertyAttributesAsWritten();
    245     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
    246     if (!TInfo)
    247       return;
    248     TypeLoc TL = TInfo->getTypeLoc();
    249     if (AttributedTypeLoc ATL =
    250             TL.getAs<AttributedTypeLoc>()) {
    251       ATLs.push_back(std::make_pair(ATL, PD));
    252       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
    253         hasWeak = true;
    254       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
    255         hasStrong = true;
    256       else
    257         return;
    258     }
    259   }
    260   if (ATLs.empty())
    261     return;
    262   if (hasWeak && hasStrong)
    263     return;
    264 
    265   TransformActions &TA = MigrateCtx.Pass.TA;
    266   Transaction Trans(TA);
    267 
    268   if (GCAttrsCollector::hasObjCImpl(
    269                               cast<Decl>(IndProps.front()->getDeclContext()))) {
    270     if (hasWeak)
    271       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
    272 
    273   } else {
    274     StringRef toAttr = "strong";
    275     if (hasWeak) {
    276       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
    277                        /*AllowOnUnkwownClass=*/true))
    278         toAttr = "weak";
    279       else
    280         toAttr = "unsafe_unretained";
    281     }
    282     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
    283       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
    284     else
    285       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
    286   }
    287 
    288   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
    289     SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
    290     if (Loc.isMacroID())
    291       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
    292                                          .getImmediateExpansionRange(Loc).first;
    293     TA.remove(Loc);
    294     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
    295     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
    296                        ATLs[i].second->getLocation());
    297     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
    298   }
    299 }
    300 
    301 static void checkAllProps(MigrationContext &MigrateCtx,
    302                           std::vector<ObjCPropertyDecl *> &AllProps) {
    303   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
    304   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
    305 
    306   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
    307     ObjCPropertyDecl *PD = AllProps[i];
    308     if (PD->getPropertyAttributesAsWritten() &
    309           (ObjCPropertyDecl::OBJC_PR_assign |
    310            ObjCPropertyDecl::OBJC_PR_readonly)) {
    311       SourceLocation AtLoc = PD->getAtLoc();
    312       if (AtLoc.isInvalid())
    313         continue;
    314       unsigned RawAt = AtLoc.getRawEncoding();
    315       AtProps[RawAt].push_back(PD);
    316     }
    317   }
    318 
    319   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
    320          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
    321     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
    322     IndivPropsTy &IndProps = I->second;
    323     checkAllAtProps(MigrateCtx, AtLoc, IndProps);
    324   }
    325 }
    326 
    327 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
    328   std::vector<ObjCPropertyDecl *> AllProps;
    329   GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
    330                                   MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
    331 
    332   errorForGCAttrsOnNonObjC(MigrateCtx);
    333   checkAllProps(MigrateCtx, AllProps);
    334   checkWeakGCAttrs(MigrateCtx);
    335 }
    336 
    337 void MigrationContext::dumpGCAttrs() {
    338   llvm::errs() << "\n################\n";
    339   for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
    340     GCAttrOccurrence &Attr = GCAttrs[i];
    341     llvm::errs() << "KIND: "
    342         << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
    343     llvm::errs() << "\nLOC: ";
    344     Attr.Loc.dump(Pass.Ctx.getSourceManager());
    345     llvm::errs() << "\nTYPE: ";
    346     Attr.ModifiedType.dump();
    347     if (Attr.Dcl) {
    348       llvm::errs() << "DECL:\n";
    349       Attr.Dcl->dump();
    350     } else {
    351       llvm::errs() << "DECL: NONE";
    352     }
    353     llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
    354     llvm::errs() << "\n----------------\n";
    355   }
    356   llvm::errs() << "\n################\n";
    357 }
    358