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
19using namespace clang;
20using namespace arcmt;
21using namespace trans;
22
23namespace {
24
25/// Collects all the places where GC attributes __strong/__weak occur.
26class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
27 MigrationContext &MigrateCtx;
28 bool FullyMigratable;
29 std::vector<ObjCPropertyDecl *> &AllProps;
30
31 typedef RecursiveASTVisitor<GCAttrsCollector> base;
32public:
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 Save(FullyMigratable, isMigratable(D));
50
51 if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(Val: D)) {
52 lookForAttribute(D: PropD, TInfo: PropD->getTypeSourceInfo());
53 AllProps.push_back(x: PropD);
54 } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(Val: D)) {
55 lookForAttribute(D: DD, TInfo: 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(TL: 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(V: 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(V: OrigLoc);
109 MigrateCtx.GCAttrs.push_back(x: 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>(Val: D))
122 return false;
123
124 if (isInMainFile(D))
125 return true;
126
127 if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Val: D))
128 return FD->hasBody();
129
130 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(Val: D))
131 return hasObjCImpl(D: ContD);
132
133 if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Val: D)) {
134 for (const auto *MI : RD->methods()) {
135 if (MI->isOutOfLine())
136 return true;
137 }
138 return false;
139 }
140
141 return isMigratable(D: cast<Decl>(Val: D->getDeclContext()));
142 }
143
144 static bool hasObjCImpl(Decl *D) {
145 if (!D)
146 return false;
147 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(Val: D)) {
148 if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(Val: ContD))
149 return ID->getImplementation() != nullptr;
150 if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(Val: ContD))
151 return CD->getImplementation() != nullptr;
152 return isa<ObjCImplDecl>(Val: 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(Loc: 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(Loc: SM.getExpansionLoc(Loc), FID: SM.getMainFileID());
174 }
175};
176
177} // anonymous namespace
178
179static 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(error: "GC managed memory will become unmanaged in ARC",
189 loc: Attr.Loc);
190 }
191 }
192 }
193}
194
195static 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(Ctx&: MigrateCtx.Pass.Ctx, type: Attr.ModifiedType,
205 /*AllowOnUnknownClass=*/true)) {
206 Transaction Trans(TA);
207 if (!MigrateCtx.RemovedAttrSet.count(V: Attr.Loc))
208 TA.replaceText(loc: Attr.Loc, text: "__weak", replacementText: "__unsafe_unretained");
209 TA.clearDiagnostic(ID1: diag::err_arc_weak_no_runtime,
210 ID2: diag::err_arc_unsupported_weak_class,
211 range: Attr.Loc);
212 }
213 }
214 }
215}
216
217typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
218
219static 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(Elt: std::make_pair(x&: ATL, y&: 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 D: cast<Decl>(Val: IndProps.front()->getDeclContext()))) {
264 if (hasWeak)
265 MigrateCtx.AtPropsWeak.insert(V: AtLoc);
266
267 } else {
268 StringRef toAttr = "strong";
269 if (hasWeak) {
270 if (canApplyWeak(Ctx&: MigrateCtx.Pass.Ctx, type: 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(fromAttr: "assign", toAttr, atLoc: AtLoc);
278 else
279 MigrateCtx.addPropertyAttribute(attr: toAttr, atLoc: 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(range: Loc);
289 TA.clearDiagnostic(IDs: diag::err_objc_property_attr_mutually_exclusive, range: AtLoc);
290 TA.clearDiagnostic(IDs: diag::err_arc_inconsistent_property_ownership,
291 range: ATLs[i].second->getLocation());
292 MigrateCtx.RemovedAttrSet.insert(V: Loc);
293 }
294}
295
296static 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(NewVal: 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
320void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
321 std::vector<ObjCPropertyDecl *> AllProps;
322 GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
323 D: MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
324
325 errorForGCAttrsOnNonObjC(MigrateCtx);
326 checkAllProps(MigrateCtx, AllProps);
327 checkWeakGCAttrs(MigrateCtx);
328}
329
330void 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(OS&: llvm::errs(), SM: 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