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