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 Save(FullyMigratable, isMigratable(D)); |
50 | |
51 | if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(Val: D)) { |
52 | lookForAttribute(PropD, PropD->getTypeSourceInfo()); |
53 | AllProps.push_back(x: PropD); |
54 | } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(Val: 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(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(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 | |
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(error: "GC managed memory will become unmanaged in ARC" , |
189 | loc: Attr.Loc); |
190 | } |
191 | } |
192 | } |
193 | } |
194 | |
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(V: Attr.Loc)) |
208 | TA.replaceText(loc: Attr.Loc, text: "__weak" , replacementText: "__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 | |
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(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>(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(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(V: Loc); |
293 | } |
294 | } |
295 | |
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(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 | |
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(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 | |