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/SmallString.h"
17 #include "llvm/ADT/TinyPtrVector.h"
18 
19 using namespace clang;
20 using namespace arcmt;
21 using namespace trans;
22 
23 namespace {
24 
25 /// \brief 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<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 (const QualifiedTypeLoc *QL = dyn_cast<QualifiedTypeLoc>(&TL)) {
66         TL = QL->getUnqualifiedLoc();
67       } else if (const AttributedTypeLoc *
68                    Attr = dyn_cast<AttributedTypeLoc>(&TL)) {
69         if (handleAttr(*Attr, D))
70           break;
71         TL = Attr->getModifiedLoc();
72       } else if (const ArrayTypeLoc *Arr = dyn_cast<ArrayTypeLoc>(&TL)) {
73         TL = Arr->getElementLoc();
74       } else if (const PointerTypeLoc *PT = dyn_cast<PointerTypeLoc>(&TL)) {
75         TL = PT->getPointeeLoc();
76       } else if (const ReferenceTypeLoc *RT = dyn_cast<ReferenceTypeLoc>(&TL))
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.getLangOptions(), &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 (Decl::redecl_iterator
168            I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I)
169       if (!isInMainFile((*I)->getLocation()))
170         return false;
171 
172     return true;
173   }
174 
175   bool isInMainFile(SourceLocation Loc) {
176     if (Loc.isInvalid())
177       return false;
178 
179     SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
180     return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
181   }
182 };
183 
184 } // anonymous namespace
185 
186 static void clearRedundantStrongs(MigrationContext &MigrateCtx) {
187   TransformActions &TA = MigrateCtx.Pass.TA;
188 
189   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
190     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
191     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Strong &&
192         Attr.FullyMigratable && Attr.Dcl) {
193       TypeSourceInfo *TInfo = 0;
194       if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(Attr.Dcl))
195         TInfo = DD->getTypeSourceInfo();
196       else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(Attr.Dcl))
197         TInfo = PD->getTypeSourceInfo();
198       if (!TInfo)
199         continue;
200 
201       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) {
202         Transaction Trans(TA);
203         TA.remove(Attr.Loc);
204         MigrateCtx.RemovedAttrSet.insert(Attr.Loc.getRawEncoding());
205       }
206     }
207   }
208 }
209 
210 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
211   TransformActions &TA = MigrateCtx.Pass.TA;
212 
213   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
214     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
215     if (Attr.FullyMigratable && Attr.Dcl) {
216       if (Attr.ModifiedType.isNull())
217         continue;
218       if (!Attr.ModifiedType->isObjCRetainableType()) {
219         TA.reportError("GC managed memory will become unmanaged in ARC",
220                        Attr.Loc);
221       }
222     }
223   }
224 }
225 
226 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
227   TransformActions &TA = MigrateCtx.Pass.TA;
228 
229   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
230     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
231     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
232       if (Attr.ModifiedType.isNull() ||
233           !Attr.ModifiedType->isObjCRetainableType())
234         continue;
235       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
236                         /*AllowOnUnknownClass=*/true)) {
237         Transaction Trans(TA);
238         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
239           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
240         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
241                            diag::err_arc_unsupported_weak_class,
242                            Attr.Loc);
243       }
244     }
245   }
246 }
247 
248 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
249 
250 static void checkAllAtProps(MigrationContext &MigrateCtx,
251                             SourceLocation AtLoc,
252                             IndivPropsTy &IndProps) {
253   if (IndProps.empty())
254     return;
255 
256   for (IndivPropsTy::iterator
257          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
258     QualType T = (*PI)->getType();
259     if (T.isNull() || !T->isObjCRetainableType())
260       return;
261   }
262 
263   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
264   bool hasWeak = false, hasStrong = false;
265   ObjCPropertyDecl::PropertyAttributeKind
266     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
267   for (IndivPropsTy::iterator
268          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
269     ObjCPropertyDecl *PD = *PI;
270     Attrs = PD->getPropertyAttributesAsWritten();
271     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
272     if (!TInfo)
273       return;
274     TypeLoc TL = TInfo->getTypeLoc();
275     if (AttributedTypeLoc *ATL = dyn_cast<AttributedTypeLoc>(&TL)) {
276       ATLs.push_back(std::make_pair(*ATL, PD));
277       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
278         hasWeak = true;
279       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
280         hasStrong = true;
281       else
282         return;
283     }
284   }
285   if (ATLs.empty())
286     return;
287   if (hasWeak && hasStrong)
288     return;
289 
290   TransformActions &TA = MigrateCtx.Pass.TA;
291   Transaction Trans(TA);
292 
293   if (GCAttrsCollector::hasObjCImpl(
294                               cast<Decl>(IndProps.front()->getDeclContext()))) {
295     if (hasWeak)
296       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
297 
298   } else {
299     StringRef toAttr = "strong";
300     if (hasWeak) {
301       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
302                        /*AllowOnUnkwownClass=*/true))
303         toAttr = "weak";
304       else
305         toAttr = "unsafe_unretained";
306     }
307     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
308       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
309     else
310       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
311   }
312 
313   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
314     SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
315     if (Loc.isMacroID())
316       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
317                                          .getImmediateExpansionRange(Loc).first;
318     TA.remove(Loc);
319     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
320     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
321                        ATLs[i].second->getLocation());
322     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
323   }
324 }
325 
326 static void checkAllProps(MigrationContext &MigrateCtx,
327                           std::vector<ObjCPropertyDecl *> &AllProps) {
328   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
329   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
330 
331   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
332     ObjCPropertyDecl *PD = AllProps[i];
333     if (PD->getPropertyAttributesAsWritten() &
334           (ObjCPropertyDecl::OBJC_PR_assign |
335            ObjCPropertyDecl::OBJC_PR_readonly)) {
336       SourceLocation AtLoc = PD->getAtLoc();
337       if (AtLoc.isInvalid())
338         continue;
339       unsigned RawAt = AtLoc.getRawEncoding();
340       AtProps[RawAt].push_back(PD);
341     }
342   }
343 
344   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
345          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
346     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
347     IndivPropsTy &IndProps = I->second;
348     checkAllAtProps(MigrateCtx, AtLoc, IndProps);
349   }
350 }
351 
352 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
353   std::vector<ObjCPropertyDecl *> AllProps;
354   GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
355                                   MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
356 
357   clearRedundantStrongs(MigrateCtx);
358   errorForGCAttrsOnNonObjC(MigrateCtx);
359   checkAllProps(MigrateCtx, AllProps);
360   checkWeakGCAttrs(MigrateCtx);
361 }
362 
363 void MigrationContext::dumpGCAttrs() {
364   llvm::errs() << "\n################\n";
365   for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
366     GCAttrOccurrence &Attr = GCAttrs[i];
367     llvm::errs() << "KIND: "
368         << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
369     llvm::errs() << "\nLOC: ";
370     Attr.Loc.dump(Pass.Ctx.getSourceManager());
371     llvm::errs() << "\nTYPE: ";
372     Attr.ModifiedType.dump();
373     if (Attr.Dcl) {
374       llvm::errs() << "DECL:\n";
375       Attr.Dcl->dump();
376     } else {
377       llvm::errs() << "DECL: NONE";
378     }
379     llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
380     llvm::errs() << "\n----------------\n";
381   }
382   llvm::errs() << "\n################\n";
383 }
384