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 /// \brief 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:
34   GCAttrsCollector(MigrationContext &ctx,
35                    std::vector<ObjCPropertyDecl *> &AllProps)
36     : MigrateCtx(ctx), FullyMigratable(false),
37       AllProps(AllProps) { }
38 
39   bool shouldWalkTypesOfTypeLocs() const { return false; }
40 
41   bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
42     handleAttr(TL);
43     return true;
44   }
45 
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 
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 
83   bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) {
84     if (TL.getAttrKind() != AttributedType::attr_objc_ownership)
85       return false;
86 
87     SourceLocation Loc = TL.getAttrNameLoc();
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).first;
96     SmallString<32> Buf;
97     bool Invalid = false;
98     StringRef Spell = Lexer::getSpelling(
99                                   SM.getSpellingLoc(TL.getAttrEnumOperandLoc()),
100                                   Buf, SM, Ctx.getLangOpts(), &Invalid);
101     if (Invalid)
102       return false;
103     MigrationContext::GCAttrOccurrence::AttrKind Kind;
104     if (Spell == "strong")
105       Kind = MigrationContext::GCAttrOccurrence::Strong;
106     else if (Spell == "weak")
107       Kind = MigrationContext::GCAttrOccurrence::Weak;
108     else
109       return false;
110 
111     MigrateCtx.AttrSet.insert(RawLoc);
112     MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
113     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
114 
115     Attr.Kind = Kind;
116     Attr.Loc = Loc;
117     Attr.ModifiedType = TL.getModifiedLoc().getType();
118     Attr.Dcl = D;
119     Attr.FullyMigratable = FullyMigratable;
120     return true;
121   }
122 
123   bool isMigratable(Decl *D) {
124     if (isa<TranslationUnitDecl>(D))
125       return false;
126 
127     if (isInMainFile(D))
128       return true;
129 
130     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
131       return FD->hasBody();
132 
133     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
134       return hasObjCImpl(ContD);
135 
136     if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
137       for (CXXRecordDecl::method_iterator
138              MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) {
139         if (MI->isOutOfLine())
140           return true;
141       }
142       return false;
143     }
144 
145     return isMigratable(cast<Decl>(D->getDeclContext()));
146   }
147 
148   static bool hasObjCImpl(Decl *D) {
149     if (!D)
150       return false;
151     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
152       if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
153         return ID->getImplementation() != 0;
154       if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
155         return CD->getImplementation() != 0;
156       if (isa<ObjCImplDecl>(ContD))
157         return true;
158       return false;
159     }
160     return false;
161   }
162 
163   bool isInMainFile(Decl *D) {
164     if (!D)
165       return false;
166 
167     for (auto I : D->redecls())
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 errorForGCAttrsOnNonObjC(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.FullyMigratable && Attr.Dcl) {
191       if (Attr.ModifiedType.isNull())
192         continue;
193       if (!Attr.ModifiedType->isObjCRetainableType()) {
194         TA.reportError("GC managed memory will become unmanaged in ARC",
195                        Attr.Loc);
196       }
197     }
198   }
199 }
200 
201 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
202   TransformActions &TA = MigrateCtx.Pass.TA;
203 
204   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
205     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
206     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
207       if (Attr.ModifiedType.isNull() ||
208           !Attr.ModifiedType->isObjCRetainableType())
209         continue;
210       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
211                         /*AllowOnUnknownClass=*/true)) {
212         Transaction Trans(TA);
213         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
214           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
215         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
216                            diag::err_arc_unsupported_weak_class,
217                            Attr.Loc);
218       }
219     }
220   }
221 }
222 
223 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
224 
225 static void checkAllAtProps(MigrationContext &MigrateCtx,
226                             SourceLocation AtLoc,
227                             IndivPropsTy &IndProps) {
228   if (IndProps.empty())
229     return;
230 
231   for (IndivPropsTy::iterator
232          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
233     QualType T = (*PI)->getType();
234     if (T.isNull() || !T->isObjCRetainableType())
235       return;
236   }
237 
238   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
239   bool hasWeak = false, hasStrong = false;
240   ObjCPropertyDecl::PropertyAttributeKind
241     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
242   for (IndivPropsTy::iterator
243          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
244     ObjCPropertyDecl *PD = *PI;
245     Attrs = PD->getPropertyAttributesAsWritten();
246     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
247     if (!TInfo)
248       return;
249     TypeLoc TL = TInfo->getTypeLoc();
250     if (AttributedTypeLoc ATL =
251             TL.getAs<AttributedTypeLoc>()) {
252       ATLs.push_back(std::make_pair(ATL, PD));
253       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
254         hasWeak = true;
255       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
256         hasStrong = true;
257       else
258         return;
259     }
260   }
261   if (ATLs.empty())
262     return;
263   if (hasWeak && hasStrong)
264     return;
265 
266   TransformActions &TA = MigrateCtx.Pass.TA;
267   Transaction Trans(TA);
268 
269   if (GCAttrsCollector::hasObjCImpl(
270                               cast<Decl>(IndProps.front()->getDeclContext()))) {
271     if (hasWeak)
272       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
273 
274   } else {
275     StringRef toAttr = "strong";
276     if (hasWeak) {
277       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
278                        /*AllowOnUnkwownClass=*/true))
279         toAttr = "weak";
280       else
281         toAttr = "unsafe_unretained";
282     }
283     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
284       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
285     else
286       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
287   }
288 
289   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
290     SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
291     if (Loc.isMacroID())
292       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
293                                          .getImmediateExpansionRange(Loc).first;
294     TA.remove(Loc);
295     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
296     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
297                        ATLs[i].second->getLocation());
298     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
299   }
300 }
301 
302 static void checkAllProps(MigrationContext &MigrateCtx,
303                           std::vector<ObjCPropertyDecl *> &AllProps) {
304   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
305   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
306 
307   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
308     ObjCPropertyDecl *PD = AllProps[i];
309     if (PD->getPropertyAttributesAsWritten() &
310           (ObjCPropertyDecl::OBJC_PR_assign |
311            ObjCPropertyDecl::OBJC_PR_readonly)) {
312       SourceLocation AtLoc = PD->getAtLoc();
313       if (AtLoc.isInvalid())
314         continue;
315       unsigned RawAt = AtLoc.getRawEncoding();
316       AtProps[RawAt].push_back(PD);
317     }
318   }
319 
320   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
321          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
322     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
323     IndivPropsTy &IndProps = I->second;
324     checkAllAtProps(MigrateCtx, AtLoc, IndProps);
325   }
326 }
327 
328 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
329   std::vector<ObjCPropertyDecl *> AllProps;
330   GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
331                                   MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
332 
333   errorForGCAttrsOnNonObjC(MigrateCtx);
334   checkAllProps(MigrateCtx, AllProps);
335   checkWeakGCAttrs(MigrateCtx);
336 }
337 
338 void MigrationContext::dumpGCAttrs() {
339   llvm::errs() << "\n################\n";
340   for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
341     GCAttrOccurrence &Attr = GCAttrs[i];
342     llvm::errs() << "KIND: "
343         << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
344     llvm::errs() << "\nLOC: ";
345     Attr.Loc.dump(Pass.Ctx.getSourceManager());
346     llvm::errs() << "\nTYPE: ";
347     Attr.ModifiedType.dump();
348     if (Attr.Dcl) {
349       llvm::errs() << "DECL:\n";
350       Attr.Dcl->dump();
351     } else {
352       llvm::errs() << "DECL: NONE";
353     }
354     llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
355     llvm::errs() << "\n----------------\n";
356   }
357   llvm::errs() << "\n################\n";
358 }
359