|  | //===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Transforms.h" | 
|  | #include "Internals.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  | #include "clang/Sema/SemaDiagnostic.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/ADT/TinyPtrVector.h" | 
|  | #include "llvm/Support/SaveAndRestore.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace arcmt; | 
|  | using namespace trans; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | /// Collects all the places where GC attributes __strong/__weak occur. | 
|  | class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> { | 
|  | MigrationContext &MigrateCtx; | 
|  | bool FullyMigratable; | 
|  | std::vector<ObjCPropertyDecl *> &AllProps; | 
|  |  | 
|  | typedef RecursiveASTVisitor<GCAttrsCollector> base; | 
|  | public: | 
|  | GCAttrsCollector(MigrationContext &ctx, | 
|  | std::vector<ObjCPropertyDecl *> &AllProps) | 
|  | : MigrateCtx(ctx), FullyMigratable(false), | 
|  | AllProps(AllProps) { } | 
|  |  | 
|  | bool shouldWalkTypesOfTypeLocs() const { return false; } | 
|  |  | 
|  | bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { | 
|  | handleAttr(TL); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseDecl(Decl *D) { | 
|  | if (!D || D->isImplicit()) | 
|  | return true; | 
|  |  | 
|  | SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D)); | 
|  |  | 
|  | if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) { | 
|  | lookForAttribute(PropD, PropD->getTypeSourceInfo()); | 
|  | AllProps.push_back(PropD); | 
|  | } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { | 
|  | lookForAttribute(DD, DD->getTypeSourceInfo()); | 
|  | } | 
|  | return base::TraverseDecl(D); | 
|  | } | 
|  |  | 
|  | void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { | 
|  | if (!TInfo) | 
|  | return; | 
|  | TypeLoc TL = TInfo->getTypeLoc(); | 
|  | while (TL) { | 
|  | if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) { | 
|  | TL = QL.getUnqualifiedLoc(); | 
|  | } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) { | 
|  | if (handleAttr(Attr, D)) | 
|  | break; | 
|  | TL = Attr.getModifiedLoc(); | 
|  | } else if (MacroQualifiedTypeLoc MDTL = | 
|  | TL.getAs<MacroQualifiedTypeLoc>()) { | 
|  | TL = MDTL.getInnerLoc(); | 
|  | } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) { | 
|  | TL = Arr.getElementLoc(); | 
|  | } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) { | 
|  | TL = PT.getPointeeLoc(); | 
|  | } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>()) | 
|  | TL = RT.getPointeeLoc(); | 
|  | else | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) { | 
|  | auto *OwnershipAttr = TL.getAttrAs<ObjCOwnershipAttr>(); | 
|  | if (!OwnershipAttr) | 
|  | return false; | 
|  |  | 
|  | SourceLocation Loc = OwnershipAttr->getLocation(); | 
|  | unsigned RawLoc = Loc.getRawEncoding(); | 
|  | if (MigrateCtx.AttrSet.count(RawLoc)) | 
|  | return true; | 
|  |  | 
|  | ASTContext &Ctx = MigrateCtx.Pass.Ctx; | 
|  | SourceManager &SM = Ctx.getSourceManager(); | 
|  | if (Loc.isMacroID()) | 
|  | Loc = SM.getImmediateExpansionRange(Loc).getBegin(); | 
|  | StringRef Spell = OwnershipAttr->getKind()->getName(); | 
|  | MigrationContext::GCAttrOccurrence::AttrKind Kind; | 
|  | if (Spell == "strong") | 
|  | Kind = MigrationContext::GCAttrOccurrence::Strong; | 
|  | else if (Spell == "weak") | 
|  | Kind = MigrationContext::GCAttrOccurrence::Weak; | 
|  | else | 
|  | return false; | 
|  |  | 
|  | MigrateCtx.AttrSet.insert(RawLoc); | 
|  | MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); | 
|  | MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); | 
|  |  | 
|  | Attr.Kind = Kind; | 
|  | Attr.Loc = Loc; | 
|  | Attr.ModifiedType = TL.getModifiedLoc().getType(); | 
|  | Attr.Dcl = D; | 
|  | Attr.FullyMigratable = FullyMigratable; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool isMigratable(Decl *D) { | 
|  | if (isa<TranslationUnitDecl>(D)) | 
|  | return false; | 
|  |  | 
|  | if (isInMainFile(D)) | 
|  | return true; | 
|  |  | 
|  | if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) | 
|  | return FD->hasBody(); | 
|  |  | 
|  | if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) | 
|  | return hasObjCImpl(ContD); | 
|  |  | 
|  | if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { | 
|  | for (const auto *MI : RD->methods()) { | 
|  | if (MI->isOutOfLine()) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return isMigratable(cast<Decl>(D->getDeclContext())); | 
|  | } | 
|  |  | 
|  | static bool hasObjCImpl(Decl *D) { | 
|  | if (!D) | 
|  | return false; | 
|  | if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) { | 
|  | if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD)) | 
|  | return ID->getImplementation() != nullptr; | 
|  | if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD)) | 
|  | return CD->getImplementation() != nullptr; | 
|  | return isa<ObjCImplDecl>(ContD); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool isInMainFile(Decl *D) { | 
|  | if (!D) | 
|  | return false; | 
|  |  | 
|  | for (auto I : D->redecls()) | 
|  | if (!isInMainFile(I->getLocation())) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool isInMainFile(SourceLocation Loc) { | 
|  | if (Loc.isInvalid()) | 
|  | return false; | 
|  |  | 
|  | SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); | 
|  | return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) { | 
|  | TransformActions &TA = MigrateCtx.Pass.TA; | 
|  |  | 
|  | for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { | 
|  | MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; | 
|  | if (Attr.FullyMigratable && Attr.Dcl) { | 
|  | if (Attr.ModifiedType.isNull()) | 
|  | continue; | 
|  | if (!Attr.ModifiedType->isObjCRetainableType()) { | 
|  | TA.reportError("GC managed memory will become unmanaged in ARC", | 
|  | Attr.Loc); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void checkWeakGCAttrs(MigrationContext &MigrateCtx) { | 
|  | TransformActions &TA = MigrateCtx.Pass.TA; | 
|  |  | 
|  | for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { | 
|  | MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; | 
|  | if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) { | 
|  | if (Attr.ModifiedType.isNull() || | 
|  | !Attr.ModifiedType->isObjCRetainableType()) | 
|  | continue; | 
|  | if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType, | 
|  | /*AllowOnUnknownClass=*/true)) { | 
|  | Transaction Trans(TA); | 
|  | if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding())) | 
|  | TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained"); | 
|  | TA.clearDiagnostic(diag::err_arc_weak_no_runtime, | 
|  | diag::err_arc_unsupported_weak_class, | 
|  | Attr.Loc); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; | 
|  |  | 
|  | static void checkAllAtProps(MigrationContext &MigrateCtx, | 
|  | SourceLocation AtLoc, | 
|  | IndivPropsTy &IndProps) { | 
|  | if (IndProps.empty()) | 
|  | return; | 
|  |  | 
|  | for (IndivPropsTy::iterator | 
|  | PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { | 
|  | QualType T = (*PI)->getType(); | 
|  | if (T.isNull() || !T->isObjCRetainableType()) | 
|  | return; | 
|  | } | 
|  |  | 
|  | SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs; | 
|  | bool hasWeak = false, hasStrong = false; | 
|  | ObjCPropertyDecl::PropertyAttributeKind | 
|  | Attrs = ObjCPropertyDecl::OBJC_PR_noattr; | 
|  | for (IndivPropsTy::iterator | 
|  | PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { | 
|  | ObjCPropertyDecl *PD = *PI; | 
|  | Attrs = PD->getPropertyAttributesAsWritten(); | 
|  | TypeSourceInfo *TInfo = PD->getTypeSourceInfo(); | 
|  | if (!TInfo) | 
|  | return; | 
|  | TypeLoc TL = TInfo->getTypeLoc(); | 
|  | if (AttributedTypeLoc ATL = | 
|  | TL.getAs<AttributedTypeLoc>()) { | 
|  | ATLs.push_back(std::make_pair(ATL, PD)); | 
|  | if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { | 
|  | hasWeak = true; | 
|  | } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) | 
|  | hasStrong = true; | 
|  | else | 
|  | return; | 
|  | } | 
|  | } | 
|  | if (ATLs.empty()) | 
|  | return; | 
|  | if (hasWeak && hasStrong) | 
|  | return; | 
|  |  | 
|  | TransformActions &TA = MigrateCtx.Pass.TA; | 
|  | Transaction Trans(TA); | 
|  |  | 
|  | if (GCAttrsCollector::hasObjCImpl( | 
|  | cast<Decl>(IndProps.front()->getDeclContext()))) { | 
|  | if (hasWeak) | 
|  | MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding()); | 
|  |  | 
|  | } else { | 
|  | StringRef toAttr = "strong"; | 
|  | if (hasWeak) { | 
|  | if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(), | 
|  | /*AllowOnUnkwownClass=*/true)) | 
|  | toAttr = "weak"; | 
|  | else | 
|  | toAttr = "unsafe_unretained"; | 
|  | } | 
|  | if (Attrs & ObjCPropertyDecl::OBJC_PR_assign) | 
|  | MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc); | 
|  | else | 
|  | MigrateCtx.addPropertyAttribute(toAttr, AtLoc); | 
|  | } | 
|  |  | 
|  | for (unsigned i = 0, e = ATLs.size(); i != e; ++i) { | 
|  | SourceLocation Loc = ATLs[i].first.getAttr()->getLocation(); | 
|  | if (Loc.isMacroID()) | 
|  | Loc = MigrateCtx.Pass.Ctx.getSourceManager() | 
|  | .getImmediateExpansionRange(Loc) | 
|  | .getBegin(); | 
|  | TA.remove(Loc); | 
|  | TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc); | 
|  | TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership, | 
|  | ATLs[i].second->getLocation()); | 
|  | MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding()); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void checkAllProps(MigrationContext &MigrateCtx, | 
|  | std::vector<ObjCPropertyDecl *> &AllProps) { | 
|  | typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; | 
|  | llvm::DenseMap<unsigned, IndivPropsTy> AtProps; | 
|  |  | 
|  | for (unsigned i = 0, e = AllProps.size(); i != e; ++i) { | 
|  | ObjCPropertyDecl *PD = AllProps[i]; | 
|  | if (PD->getPropertyAttributesAsWritten() & | 
|  | (ObjCPropertyDecl::OBJC_PR_assign | | 
|  | ObjCPropertyDecl::OBJC_PR_readonly)) { | 
|  | SourceLocation AtLoc = PD->getAtLoc(); | 
|  | if (AtLoc.isInvalid()) | 
|  | continue; | 
|  | unsigned RawAt = AtLoc.getRawEncoding(); | 
|  | AtProps[RawAt].push_back(PD); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator | 
|  | I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { | 
|  | SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first); | 
|  | IndivPropsTy &IndProps = I->second; | 
|  | checkAllAtProps(MigrateCtx, AtLoc, IndProps); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { | 
|  | std::vector<ObjCPropertyDecl *> AllProps; | 
|  | GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl( | 
|  | MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); | 
|  |  | 
|  | errorForGCAttrsOnNonObjC(MigrateCtx); | 
|  | checkAllProps(MigrateCtx, AllProps); | 
|  | checkWeakGCAttrs(MigrateCtx); | 
|  | } | 
|  |  | 
|  | void MigrationContext::dumpGCAttrs() { | 
|  | llvm::errs() << "\n################\n"; | 
|  | for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) { | 
|  | GCAttrOccurrence &Attr = GCAttrs[i]; | 
|  | llvm::errs() << "KIND: " | 
|  | << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak"); | 
|  | llvm::errs() << "\nLOC: "; | 
|  | Attr.Loc.print(llvm::errs(), Pass.Ctx.getSourceManager()); | 
|  | llvm::errs() << "\nTYPE: "; | 
|  | Attr.ModifiedType.dump(); | 
|  | if (Attr.Dcl) { | 
|  | llvm::errs() << "DECL:\n"; | 
|  | Attr.Dcl->dump(); | 
|  | } else { | 
|  | llvm::errs() << "DECL: NONE"; | 
|  | } | 
|  | llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; | 
|  | llvm::errs() << "\n----------------\n"; | 
|  | } | 
|  | llvm::errs() << "\n################\n"; | 
|  | } |