1 | |
2 | |
3 | |
4 | |
5 | |
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 | |
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 | } |
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 | )) { |
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 | )) |
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 | |