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:
GCAttrsCollector(MigrationContext & ctx,std::vector<ObjCPropertyDecl * > & AllProps)33   GCAttrsCollector(MigrationContext &ctx,
34                    std::vector<ObjCPropertyDecl *> &AllProps)
35     : MigrateCtx(ctx), FullyMigratable(false),
36       AllProps(AllProps) { }
37 
shouldWalkTypesOfTypeLocs() const38   bool shouldWalkTypesOfTypeLocs() const { return false; }
39 
VisitAttributedTypeLoc(AttributedTypeLoc TL)40   bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
41     handleAttr(TL);
42     return true;
43   }
44 
TraverseDecl(Decl * D)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 
lookForAttribute(Decl * D,TypeSourceInfo * TInfo)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 
handleAttr(AttributedTypeLoc TL,Decl * D=nullptr)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 
isMigratable(Decl * D)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 
hasObjCImpl(Decl * D)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 
isInMainFile(Decl * D)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 
isInMainFile(SourceLocation Loc)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 
errorForGCAttrsOnNonObjC(MigrationContext & MigrateCtx)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 
checkWeakGCAttrs(MigrationContext & MigrateCtx)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 
checkAllAtProps(MigrationContext & MigrateCtx,SourceLocation AtLoc,IndivPropsTy & IndProps)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 
checkAllProps(MigrationContext & MigrateCtx,std::vector<ObjCPropertyDecl * > & AllProps)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 
traverseTU(MigrationContext & MigrateCtx)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 
dumpGCAttrs()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