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