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