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:
GCAttrsCollector(MigrationContext & ctx,std::vector<ObjCPropertyDecl * > & AllProps)34   GCAttrsCollector(MigrationContext &ctx,
35                    std::vector<ObjCPropertyDecl *> &AllProps)
36     : MigrateCtx(ctx), FullyMigratable(false),
37       AllProps(AllProps) { }
38 
shouldWalkTypesOfTypeLocs() const39   bool shouldWalkTypesOfTypeLocs() const { return false; }
40 
VisitAttributedTypeLoc(AttributedTypeLoc TL)41   bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
42     handleAttr(TL);
43     return true;
44   }
45 
TraverseDecl(Decl * D)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 
lookForAttribute(Decl * D,TypeSourceInfo * TInfo)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 
handleAttr(AttributedTypeLoc TL,Decl * D=nullptr)83   bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) {
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 
isMigratable(Decl * D)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 (const auto *MI : RD->methods()) {
138         if (MI->isOutOfLine())
139           return true;
140       }
141       return false;
142     }
143 
144     return isMigratable(cast<Decl>(D->getDeclContext()));
145   }
146 
hasObjCImpl(Decl * D)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() != nullptr;
153       if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
154         return CD->getImplementation() != nullptr;
155       if (isa<ObjCImplDecl>(ContD))
156         return true;
157       return false;
158     }
159     return false;
160   }
161 
isInMainFile(Decl * D)162   bool isInMainFile(Decl *D) {
163     if (!D)
164       return false;
165 
166     for (auto I : D->redecls())
167       if (!isInMainFile(I->getLocation()))
168         return false;
169 
170     return true;
171   }
172 
isInMainFile(SourceLocation Loc)173   bool isInMainFile(SourceLocation Loc) {
174     if (Loc.isInvalid())
175       return false;
176 
177     SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
178     return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
179   }
180 };
181 
182 } // anonymous namespace
183 
errorForGCAttrsOnNonObjC(MigrationContext & MigrateCtx)184 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
185   TransformActions &TA = MigrateCtx.Pass.TA;
186 
187   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
188     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
189     if (Attr.FullyMigratable && Attr.Dcl) {
190       if (Attr.ModifiedType.isNull())
191         continue;
192       if (!Attr.ModifiedType->isObjCRetainableType()) {
193         TA.reportError("GC managed memory will become unmanaged in ARC",
194                        Attr.Loc);
195       }
196     }
197   }
198 }
199 
checkWeakGCAttrs(MigrationContext & MigrateCtx)200 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
201   TransformActions &TA = MigrateCtx.Pass.TA;
202 
203   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
204     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
205     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
206       if (Attr.ModifiedType.isNull() ||
207           !Attr.ModifiedType->isObjCRetainableType())
208         continue;
209       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
210                         /*AllowOnUnknownClass=*/true)) {
211         Transaction Trans(TA);
212         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
213           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
214         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
215                            diag::err_arc_unsupported_weak_class,
216                            Attr.Loc);
217       }
218     }
219   }
220 }
221 
222 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
223 
checkAllAtProps(MigrationContext & MigrateCtx,SourceLocation AtLoc,IndivPropsTy & IndProps)224 static void checkAllAtProps(MigrationContext &MigrateCtx,
225                             SourceLocation AtLoc,
226                             IndivPropsTy &IndProps) {
227   if (IndProps.empty())
228     return;
229 
230   for (IndivPropsTy::iterator
231          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
232     QualType T = (*PI)->getType();
233     if (T.isNull() || !T->isObjCRetainableType())
234       return;
235   }
236 
237   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
238   bool hasWeak = false, hasStrong = false;
239   ObjCPropertyDecl::PropertyAttributeKind
240     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
241   for (IndivPropsTy::iterator
242          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
243     ObjCPropertyDecl *PD = *PI;
244     Attrs = PD->getPropertyAttributesAsWritten();
245     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
246     if (!TInfo)
247       return;
248     TypeLoc TL = TInfo->getTypeLoc();
249     if (AttributedTypeLoc ATL =
250             TL.getAs<AttributedTypeLoc>()) {
251       ATLs.push_back(std::make_pair(ATL, PD));
252       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
253         hasWeak = true;
254       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
255         hasStrong = true;
256       else
257         return;
258     }
259   }
260   if (ATLs.empty())
261     return;
262   if (hasWeak && hasStrong)
263     return;
264 
265   TransformActions &TA = MigrateCtx.Pass.TA;
266   Transaction Trans(TA);
267 
268   if (GCAttrsCollector::hasObjCImpl(
269                               cast<Decl>(IndProps.front()->getDeclContext()))) {
270     if (hasWeak)
271       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
272 
273   } else {
274     StringRef toAttr = "strong";
275     if (hasWeak) {
276       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
277                        /*AllowOnUnkwownClass=*/true))
278         toAttr = "weak";
279       else
280         toAttr = "unsafe_unretained";
281     }
282     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
283       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
284     else
285       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
286   }
287 
288   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
289     SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
290     if (Loc.isMacroID())
291       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
292                                          .getImmediateExpansionRange(Loc).first;
293     TA.remove(Loc);
294     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
295     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
296                        ATLs[i].second->getLocation());
297     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
298   }
299 }
300 
checkAllProps(MigrationContext & MigrateCtx,std::vector<ObjCPropertyDecl * > & AllProps)301 static void checkAllProps(MigrationContext &MigrateCtx,
302                           std::vector<ObjCPropertyDecl *> &AllProps) {
303   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
304   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
305 
306   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
307     ObjCPropertyDecl *PD = AllProps[i];
308     if (PD->getPropertyAttributesAsWritten() &
309           (ObjCPropertyDecl::OBJC_PR_assign |
310            ObjCPropertyDecl::OBJC_PR_readonly)) {
311       SourceLocation AtLoc = PD->getAtLoc();
312       if (AtLoc.isInvalid())
313         continue;
314       unsigned RawAt = AtLoc.getRawEncoding();
315       AtProps[RawAt].push_back(PD);
316     }
317   }
318 
319   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
320          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
321     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
322     IndivPropsTy &IndProps = I->second;
323     checkAllAtProps(MigrateCtx, AtLoc, IndProps);
324   }
325 }
326 
traverseTU(MigrationContext & MigrateCtx)327 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
328   std::vector<ObjCPropertyDecl *> AllProps;
329   GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
330                                   MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
331 
332   errorForGCAttrsOnNonObjC(MigrateCtx);
333   checkAllProps(MigrateCtx, AllProps);
334   checkWeakGCAttrs(MigrateCtx);
335 }
336 
dumpGCAttrs()337 void MigrationContext::dumpGCAttrs() {
338   llvm::errs() << "\n################\n";
339   for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
340     GCAttrOccurrence &Attr = GCAttrs[i];
341     llvm::errs() << "KIND: "
342         << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
343     llvm::errs() << "\nLOC: ";
344     Attr.Loc.dump(Pass.Ctx.getSourceManager());
345     llvm::errs() << "\nTYPE: ";
346     Attr.ModifiedType.dump();
347     if (Attr.Dcl) {
348       llvm::errs() << "DECL:\n";
349       Attr.Dcl->dump();
350     } else {
351       llvm::errs() << "DECL: NONE";
352     }
353     llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
354     llvm::errs() << "\n----------------\n";
355   }
356   llvm::errs() << "\n################\n";
357 }
358