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 (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
72         TL = Arr.getElementLoc();
73       } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
74         TL = PT.getPointeeLoc();
75       } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
76         TL = RT.getPointeeLoc();
77       else
78         break;
79     }
80   }
81 
82   bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
83     auto *OwnershipAttr = TL.getAttrAs<ObjCOwnershipAttr>();
84     if (!OwnershipAttr)
85       return false;
86 
87     SourceLocation Loc = OwnershipAttr->getLocation();
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).getBegin();
96     StringRef Spell = OwnershipAttr->getKind()->getName();
97     MigrationContext::GCAttrOccurrence::AttrKind Kind;
98     if (Spell == "strong")
99       Kind = MigrationContext::GCAttrOccurrence::Strong;
100     else if (Spell == "weak")
101       Kind = MigrationContext::GCAttrOccurrence::Weak;
102     else
103       return false;
104 
105     MigrateCtx.AttrSet.insert(RawLoc);
106     MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
107     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
108 
109     Attr.Kind = Kind;
110     Attr.Loc = Loc;
111     Attr.ModifiedType = TL.getModifiedLoc().getType();
112     Attr.Dcl = D;
113     Attr.FullyMigratable = FullyMigratable;
114     return true;
115   }
116 
117   bool isMigratable(Decl *D) {
118     if (isa<TranslationUnitDecl>(D))
119       return false;
120 
121     if (isInMainFile(D))
122       return true;
123 
124     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
125       return FD->hasBody();
126 
127     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
128       return hasObjCImpl(ContD);
129 
130     if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
131       for (const auto *MI : RD->methods()) {
132         if (MI->isOutOfLine())
133           return true;
134       }
135       return false;
136     }
137 
138     return isMigratable(cast<Decl>(D->getDeclContext()));
139   }
140 
141   static bool hasObjCImpl(Decl *D) {
142     if (!D)
143       return false;
144     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
145       if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
146         return ID->getImplementation() != nullptr;
147       if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
148         return CD->getImplementation() != nullptr;
149       return isa<ObjCImplDecl>(ContD);
150     }
151     return false;
152   }
153 
154   bool isInMainFile(Decl *D) {
155     if (!D)
156       return false;
157 
158     for (auto I : D->redecls())
159       if (!isInMainFile(I->getLocation()))
160         return false;
161 
162     return true;
163   }
164 
165   bool isInMainFile(SourceLocation Loc) {
166     if (Loc.isInvalid())
167       return false;
168 
169     SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
170     return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
171   }
172 };
173 
174 } // anonymous namespace
175 
176 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
177   TransformActions &TA = MigrateCtx.Pass.TA;
178 
179   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
180     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
181     if (Attr.FullyMigratable && Attr.Dcl) {
182       if (Attr.ModifiedType.isNull())
183         continue;
184       if (!Attr.ModifiedType->isObjCRetainableType()) {
185         TA.reportError("GC managed memory will become unmanaged in ARC",
186                        Attr.Loc);
187       }
188     }
189   }
190 }
191 
192 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
193   TransformActions &TA = MigrateCtx.Pass.TA;
194 
195   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
196     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
197     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
198       if (Attr.ModifiedType.isNull() ||
199           !Attr.ModifiedType->isObjCRetainableType())
200         continue;
201       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
202                         /*AllowOnUnknownClass=*/true)) {
203         Transaction Trans(TA);
204         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
205           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
206         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
207                            diag::err_arc_unsupported_weak_class,
208                            Attr.Loc);
209       }
210     }
211   }
212 }
213 
214 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
215 
216 static void checkAllAtProps(MigrationContext &MigrateCtx,
217                             SourceLocation AtLoc,
218                             IndivPropsTy &IndProps) {
219   if (IndProps.empty())
220     return;
221 
222   for (IndivPropsTy::iterator
223          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
224     QualType T = (*PI)->getType();
225     if (T.isNull() || !T->isObjCRetainableType())
226       return;
227   }
228 
229   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
230   bool hasWeak = false, hasStrong = false;
231   ObjCPropertyDecl::PropertyAttributeKind
232     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
233   for (IndivPropsTy::iterator
234          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
235     ObjCPropertyDecl *PD = *PI;
236     Attrs = PD->getPropertyAttributesAsWritten();
237     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
238     if (!TInfo)
239       return;
240     TypeLoc TL = TInfo->getTypeLoc();
241     if (AttributedTypeLoc ATL =
242             TL.getAs<AttributedTypeLoc>()) {
243       ATLs.push_back(std::make_pair(ATL, PD));
244       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
245         hasWeak = true;
246       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
247         hasStrong = true;
248       else
249         return;
250     }
251   }
252   if (ATLs.empty())
253     return;
254   if (hasWeak && hasStrong)
255     return;
256 
257   TransformActions &TA = MigrateCtx.Pass.TA;
258   Transaction Trans(TA);
259 
260   if (GCAttrsCollector::hasObjCImpl(
261                               cast<Decl>(IndProps.front()->getDeclContext()))) {
262     if (hasWeak)
263       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
264 
265   } else {
266     StringRef toAttr = "strong";
267     if (hasWeak) {
268       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
269                        /*AllowOnUnkwownClass=*/true))
270         toAttr = "weak";
271       else
272         toAttr = "unsafe_unretained";
273     }
274     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
275       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
276     else
277       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
278   }
279 
280   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
281     SourceLocation Loc = ATLs[i].first.getAttr()->getLocation();
282     if (Loc.isMacroID())
283       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
284                 .getImmediateExpansionRange(Loc)
285                 .getBegin();
286     TA.remove(Loc);
287     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
288     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
289                        ATLs[i].second->getLocation());
290     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
291   }
292 }
293 
294 static void checkAllProps(MigrationContext &MigrateCtx,
295                           std::vector<ObjCPropertyDecl *> &AllProps) {
296   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
297   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
298 
299   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
300     ObjCPropertyDecl *PD = AllProps[i];
301     if (PD->getPropertyAttributesAsWritten() &
302           (ObjCPropertyDecl::OBJC_PR_assign |
303            ObjCPropertyDecl::OBJC_PR_readonly)) {
304       SourceLocation AtLoc = PD->getAtLoc();
305       if (AtLoc.isInvalid())
306         continue;
307       unsigned RawAt = AtLoc.getRawEncoding();
308       AtProps[RawAt].push_back(PD);
309     }
310   }
311 
312   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
313          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
314     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(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