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