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