xref: /aosp_15_r20/external/clang/lib/ARCMigrate/TransProperties.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //===--- TransProperties.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 // rewriteProperties:
11*67e74705SXin Li //
12*67e74705SXin Li // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
13*67e74705SXin Li //   are missing one.
14*67e74705SXin Li // - Migrates properties from (retain) to (strong) and (assign) to
15*67e74705SXin Li //   (unsafe_unretained/weak).
16*67e74705SXin Li // - If a property is synthesized, adds the ownership specifier in the ivar
17*67e74705SXin Li //   backing the property.
18*67e74705SXin Li //
19*67e74705SXin Li //  @interface Foo : NSObject {
20*67e74705SXin Li //      NSObject *x;
21*67e74705SXin Li //  }
22*67e74705SXin Li //  @property (assign) id x;
23*67e74705SXin Li //  @end
24*67e74705SXin Li // ---->
25*67e74705SXin Li //  @interface Foo : NSObject {
26*67e74705SXin Li //      NSObject *__weak x;
27*67e74705SXin Li //  }
28*67e74705SXin Li //  @property (weak) id x;
29*67e74705SXin Li //  @end
30*67e74705SXin Li //
31*67e74705SXin Li //===----------------------------------------------------------------------===//
32*67e74705SXin Li 
33*67e74705SXin Li #include "Transforms.h"
34*67e74705SXin Li #include "Internals.h"
35*67e74705SXin Li #include "clang/Basic/SourceManager.h"
36*67e74705SXin Li #include "clang/Lex/Lexer.h"
37*67e74705SXin Li #include "clang/Sema/SemaDiagnostic.h"
38*67e74705SXin Li #include <map>
39*67e74705SXin Li 
40*67e74705SXin Li using namespace clang;
41*67e74705SXin Li using namespace arcmt;
42*67e74705SXin Li using namespace trans;
43*67e74705SXin Li 
44*67e74705SXin Li namespace {
45*67e74705SXin Li 
46*67e74705SXin Li class PropertiesRewriter {
47*67e74705SXin Li   MigrationContext &MigrateCtx;
48*67e74705SXin Li   MigrationPass &Pass;
49*67e74705SXin Li   ObjCImplementationDecl *CurImplD;
50*67e74705SXin Li 
51*67e74705SXin Li   enum PropActionKind {
52*67e74705SXin Li     PropAction_None,
53*67e74705SXin Li     PropAction_RetainReplacedWithStrong,
54*67e74705SXin Li     PropAction_AssignRemoved,
55*67e74705SXin Li     PropAction_AssignRewritten,
56*67e74705SXin Li     PropAction_MaybeAddWeakOrUnsafe
57*67e74705SXin Li   };
58*67e74705SXin Li 
59*67e74705SXin Li   struct PropData {
60*67e74705SXin Li     ObjCPropertyDecl *PropD;
61*67e74705SXin Li     ObjCIvarDecl *IvarD;
62*67e74705SXin Li     ObjCPropertyImplDecl *ImplD;
63*67e74705SXin Li 
PropData__anona36ea4150111::PropertiesRewriter::PropData64*67e74705SXin Li     PropData(ObjCPropertyDecl *propD)
65*67e74705SXin Li       : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
66*67e74705SXin Li   };
67*67e74705SXin Li 
68*67e74705SXin Li   typedef SmallVector<PropData, 2> PropsTy;
69*67e74705SXin Li   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
70*67e74705SXin Li   AtPropDeclsTy AtProps;
71*67e74705SXin Li   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
72*67e74705SXin Li 
73*67e74705SXin Li public:
PropertiesRewriter(MigrationContext & MigrateCtx)74*67e74705SXin Li   explicit PropertiesRewriter(MigrationContext &MigrateCtx)
75*67e74705SXin Li     : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
76*67e74705SXin Li 
collectProperties(ObjCContainerDecl * D,AtPropDeclsTy & AtProps,AtPropDeclsTy * PrevAtProps=nullptr)77*67e74705SXin Li   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
78*67e74705SXin Li                                 AtPropDeclsTy *PrevAtProps = nullptr) {
79*67e74705SXin Li     for (auto *Prop : D->instance_properties()) {
80*67e74705SXin Li       if (Prop->getAtLoc().isInvalid())
81*67e74705SXin Li         continue;
82*67e74705SXin Li       unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
83*67e74705SXin Li       if (PrevAtProps)
84*67e74705SXin Li         if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
85*67e74705SXin Li           continue;
86*67e74705SXin Li       PropsTy &props = AtProps[RawLoc];
87*67e74705SXin Li       props.push_back(Prop);
88*67e74705SXin Li     }
89*67e74705SXin Li   }
90*67e74705SXin Li 
doTransform(ObjCImplementationDecl * D)91*67e74705SXin Li   void doTransform(ObjCImplementationDecl *D) {
92*67e74705SXin Li     CurImplD = D;
93*67e74705SXin Li     ObjCInterfaceDecl *iface = D->getClassInterface();
94*67e74705SXin Li     if (!iface)
95*67e74705SXin Li       return;
96*67e74705SXin Li 
97*67e74705SXin Li     collectProperties(iface, AtProps);
98*67e74705SXin Li 
99*67e74705SXin Li     // Look through extensions.
100*67e74705SXin Li     for (auto *Ext : iface->visible_extensions())
101*67e74705SXin Li       collectProperties(Ext, AtProps);
102*67e74705SXin Li 
103*67e74705SXin Li     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
104*67e74705SXin Li         prop_impl_iterator;
105*67e74705SXin Li     for (prop_impl_iterator
106*67e74705SXin Li            I = prop_impl_iterator(D->decls_begin()),
107*67e74705SXin Li            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
108*67e74705SXin Li       ObjCPropertyImplDecl *implD = *I;
109*67e74705SXin Li       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
110*67e74705SXin Li         continue;
111*67e74705SXin Li       ObjCPropertyDecl *propD = implD->getPropertyDecl();
112*67e74705SXin Li       if (!propD || propD->isInvalidDecl())
113*67e74705SXin Li         continue;
114*67e74705SXin Li       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
115*67e74705SXin Li       if (!ivarD || ivarD->isInvalidDecl())
116*67e74705SXin Li         continue;
117*67e74705SXin Li       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
118*67e74705SXin Li       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
119*67e74705SXin Li       if (findAtLoc == AtProps.end())
120*67e74705SXin Li         continue;
121*67e74705SXin Li 
122*67e74705SXin Li       PropsTy &props = findAtLoc->second;
123*67e74705SXin Li       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
124*67e74705SXin Li         if (I->PropD == propD) {
125*67e74705SXin Li           I->IvarD = ivarD;
126*67e74705SXin Li           I->ImplD = implD;
127*67e74705SXin Li           break;
128*67e74705SXin Li         }
129*67e74705SXin Li       }
130*67e74705SXin Li     }
131*67e74705SXin Li 
132*67e74705SXin Li     for (AtPropDeclsTy::iterator
133*67e74705SXin Li            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
134*67e74705SXin Li       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
135*67e74705SXin Li       PropsTy &props = I->second;
136*67e74705SXin Li       if (!getPropertyType(props)->isObjCRetainableType())
137*67e74705SXin Li         continue;
138*67e74705SXin Li       if (hasIvarWithExplicitARCOwnership(props))
139*67e74705SXin Li         continue;
140*67e74705SXin Li 
141*67e74705SXin Li       Transaction Trans(Pass.TA);
142*67e74705SXin Li       rewriteProperty(props, atLoc);
143*67e74705SXin Li     }
144*67e74705SXin Li   }
145*67e74705SXin Li 
146*67e74705SXin Li private:
doPropAction(PropActionKind kind,PropsTy & props,SourceLocation atLoc,bool markAction=true)147*67e74705SXin Li   void doPropAction(PropActionKind kind,
148*67e74705SXin Li                     PropsTy &props, SourceLocation atLoc,
149*67e74705SXin Li                     bool markAction = true) {
150*67e74705SXin Li     if (markAction)
151*67e74705SXin Li       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
152*67e74705SXin Li         ActionOnProp[I->PropD->getIdentifier()] = kind;
153*67e74705SXin Li 
154*67e74705SXin Li     switch (kind) {
155*67e74705SXin Li     case PropAction_None:
156*67e74705SXin Li       return;
157*67e74705SXin Li     case PropAction_RetainReplacedWithStrong: {
158*67e74705SXin Li       StringRef toAttr = "strong";
159*67e74705SXin Li       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
160*67e74705SXin Li       return;
161*67e74705SXin Li     }
162*67e74705SXin Li     case PropAction_AssignRemoved:
163*67e74705SXin Li       return removeAssignForDefaultStrong(props, atLoc);
164*67e74705SXin Li     case PropAction_AssignRewritten:
165*67e74705SXin Li       return rewriteAssign(props, atLoc);
166*67e74705SXin Li     case PropAction_MaybeAddWeakOrUnsafe:
167*67e74705SXin Li       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
168*67e74705SXin Li     }
169*67e74705SXin Li   }
170*67e74705SXin Li 
rewriteProperty(PropsTy & props,SourceLocation atLoc)171*67e74705SXin Li   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
172*67e74705SXin Li     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
173*67e74705SXin Li 
174*67e74705SXin Li     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
175*67e74705SXin Li                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
176*67e74705SXin Li                      ObjCPropertyDecl::OBJC_PR_strong |
177*67e74705SXin Li                      ObjCPropertyDecl::OBJC_PR_weak))
178*67e74705SXin Li       return;
179*67e74705SXin Li 
180*67e74705SXin Li     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
181*67e74705SXin Li       // strong is the default.
182*67e74705SXin Li       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
183*67e74705SXin Li     }
184*67e74705SXin Li 
185*67e74705SXin Li     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
186*67e74705SXin Li 
187*67e74705SXin Li     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
188*67e74705SXin Li       if (HasIvarAssignedAPlusOneObject)
189*67e74705SXin Li         return doPropAction(PropAction_AssignRemoved, props, atLoc);
190*67e74705SXin Li       return doPropAction(PropAction_AssignRewritten, props, atLoc);
191*67e74705SXin Li     }
192*67e74705SXin Li 
193*67e74705SXin Li     if (HasIvarAssignedAPlusOneObject ||
194*67e74705SXin Li         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
195*67e74705SXin Li       return; // 'strong' by default.
196*67e74705SXin Li 
197*67e74705SXin Li     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
198*67e74705SXin Li   }
199*67e74705SXin Li 
removeAssignForDefaultStrong(PropsTy & props,SourceLocation atLoc) const200*67e74705SXin Li   void removeAssignForDefaultStrong(PropsTy &props,
201*67e74705SXin Li                                     SourceLocation atLoc) const {
202*67e74705SXin Li     removeAttribute("retain", atLoc);
203*67e74705SXin Li     if (!removeAttribute("assign", atLoc))
204*67e74705SXin Li       return;
205*67e74705SXin Li 
206*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
207*67e74705SXin Li       if (I->ImplD)
208*67e74705SXin Li         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
209*67e74705SXin Li                                 diag::err_arc_assign_property_ownership,
210*67e74705SXin Li                                 diag::err_arc_inconsistent_property_ownership,
211*67e74705SXin Li                                 I->IvarD->getLocation());
212*67e74705SXin Li     }
213*67e74705SXin Li   }
214*67e74705SXin Li 
rewriteAssign(PropsTy & props,SourceLocation atLoc) const215*67e74705SXin Li   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
216*67e74705SXin Li     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
217*67e74705SXin Li                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
218*67e74705SXin Li     const char *toWhich =
219*67e74705SXin Li       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
220*67e74705SXin Li       (canUseWeak ? "weak" : "unsafe_unretained");
221*67e74705SXin Li 
222*67e74705SXin Li     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
223*67e74705SXin Li     if (!rewroteAttr)
224*67e74705SXin Li       canUseWeak = false;
225*67e74705SXin Li 
226*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
227*67e74705SXin Li       if (isUserDeclared(I->IvarD)) {
228*67e74705SXin Li         if (I->IvarD &&
229*67e74705SXin Li             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
230*67e74705SXin Li           const char *toWhich =
231*67e74705SXin Li             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
232*67e74705SXin Li               (canUseWeak ? "__weak " : "__unsafe_unretained ");
233*67e74705SXin Li           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
234*67e74705SXin Li         }
235*67e74705SXin Li       }
236*67e74705SXin Li       if (I->ImplD)
237*67e74705SXin Li         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
238*67e74705SXin Li                                 diag::err_arc_assign_property_ownership,
239*67e74705SXin Li                                 diag::err_arc_inconsistent_property_ownership,
240*67e74705SXin Li                                 I->IvarD->getLocation());
241*67e74705SXin Li     }
242*67e74705SXin Li   }
243*67e74705SXin Li 
maybeAddWeakOrUnsafeUnretainedAttr(PropsTy & props,SourceLocation atLoc) const244*67e74705SXin Li   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
245*67e74705SXin Li                                           SourceLocation atLoc) const {
246*67e74705SXin Li     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
247*67e74705SXin Li                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
248*67e74705SXin Li 
249*67e74705SXin Li     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
250*67e74705SXin Li                                   atLoc);
251*67e74705SXin Li     if (!addedAttr)
252*67e74705SXin Li       canUseWeak = false;
253*67e74705SXin Li 
254*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
255*67e74705SXin Li       if (isUserDeclared(I->IvarD)) {
256*67e74705SXin Li         if (I->IvarD &&
257*67e74705SXin Li             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
258*67e74705SXin Li           Pass.TA.insert(I->IvarD->getLocation(),
259*67e74705SXin Li                          canUseWeak ? "__weak " : "__unsafe_unretained ");
260*67e74705SXin Li       }
261*67e74705SXin Li       if (I->ImplD) {
262*67e74705SXin Li         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
263*67e74705SXin Li                                 diag::err_arc_assign_property_ownership,
264*67e74705SXin Li                                 diag::err_arc_inconsistent_property_ownership,
265*67e74705SXin Li                                 I->IvarD->getLocation());
266*67e74705SXin Li         Pass.TA.clearDiagnostic(
267*67e74705SXin Li                            diag::err_arc_objc_property_default_assign_on_object,
268*67e74705SXin Li                            I->ImplD->getLocation());
269*67e74705SXin Li       }
270*67e74705SXin Li     }
271*67e74705SXin Li   }
272*67e74705SXin Li 
removeAttribute(StringRef fromAttr,SourceLocation atLoc) const273*67e74705SXin Li   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
274*67e74705SXin Li     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
275*67e74705SXin Li   }
276*67e74705SXin Li 
rewriteAttribute(StringRef fromAttr,StringRef toAttr,SourceLocation atLoc) const277*67e74705SXin Li   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
278*67e74705SXin Li                         SourceLocation atLoc) const {
279*67e74705SXin Li     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
280*67e74705SXin Li   }
281*67e74705SXin Li 
addAttribute(StringRef attr,SourceLocation atLoc) const282*67e74705SXin Li   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
283*67e74705SXin Li     return MigrateCtx.addPropertyAttribute(attr, atLoc);
284*67e74705SXin Li   }
285*67e74705SXin Li 
286*67e74705SXin Li   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
287*67e74705SXin Li     ObjCIvarDecl *Ivar;
288*67e74705SXin Li   public:
PlusOneAssign(ObjCIvarDecl * D)289*67e74705SXin Li     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
290*67e74705SXin Li 
VisitBinAssign(BinaryOperator * E)291*67e74705SXin Li     bool VisitBinAssign(BinaryOperator *E) {
292*67e74705SXin Li       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
293*67e74705SXin Li       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
294*67e74705SXin Li         if (RE->getDecl() != Ivar)
295*67e74705SXin Li           return true;
296*67e74705SXin Li 
297*67e74705SXin Li         if (isPlusOneAssign(E))
298*67e74705SXin Li           return false;
299*67e74705SXin Li       }
300*67e74705SXin Li 
301*67e74705SXin Li       return true;
302*67e74705SXin Li     }
303*67e74705SXin Li   };
304*67e74705SXin Li 
hasIvarAssignedAPlusOneObject(PropsTy & props) const305*67e74705SXin Li   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
306*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
307*67e74705SXin Li       PlusOneAssign oneAssign(I->IvarD);
308*67e74705SXin Li       bool notFound = oneAssign.TraverseDecl(CurImplD);
309*67e74705SXin Li       if (!notFound)
310*67e74705SXin Li         return true;
311*67e74705SXin Li     }
312*67e74705SXin Li 
313*67e74705SXin Li     return false;
314*67e74705SXin Li   }
315*67e74705SXin Li 
hasIvarWithExplicitARCOwnership(PropsTy & props) const316*67e74705SXin Li   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
317*67e74705SXin Li     if (Pass.isGCMigration())
318*67e74705SXin Li       return false;
319*67e74705SXin Li 
320*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
321*67e74705SXin Li       if (isUserDeclared(I->IvarD)) {
322*67e74705SXin Li         if (isa<AttributedType>(I->IvarD->getType()))
323*67e74705SXin Li           return true;
324*67e74705SXin Li         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
325*67e74705SXin Li               != Qualifiers::OCL_Strong)
326*67e74705SXin Li           return true;
327*67e74705SXin Li       }
328*67e74705SXin Li     }
329*67e74705SXin Li 
330*67e74705SXin Li     return false;
331*67e74705SXin Li   }
332*67e74705SXin Li 
333*67e74705SXin Li   // \brief Returns true if all declarations in the @property have GC __weak.
hasGCWeak(PropsTy & props,SourceLocation atLoc) const334*67e74705SXin Li   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
335*67e74705SXin Li     if (!Pass.isGCMigration())
336*67e74705SXin Li       return false;
337*67e74705SXin Li     if (props.empty())
338*67e74705SXin Li       return false;
339*67e74705SXin Li     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
340*67e74705SXin Li   }
341*67e74705SXin Li 
isUserDeclared(ObjCIvarDecl * ivarD) const342*67e74705SXin Li   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
343*67e74705SXin Li     return ivarD && !ivarD->getSynthesize();
344*67e74705SXin Li   }
345*67e74705SXin Li 
getPropertyType(PropsTy & props) const346*67e74705SXin Li   QualType getPropertyType(PropsTy &props) const {
347*67e74705SXin Li     assert(!props.empty());
348*67e74705SXin Li     QualType ty = props[0].PropD->getType().getUnqualifiedType();
349*67e74705SXin Li 
350*67e74705SXin Li #ifndef NDEBUG
351*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
352*67e74705SXin Li       assert(ty == I->PropD->getType().getUnqualifiedType());
353*67e74705SXin Li #endif
354*67e74705SXin Li 
355*67e74705SXin Li     return ty;
356*67e74705SXin Li   }
357*67e74705SXin Li 
358*67e74705SXin Li   ObjCPropertyDecl::PropertyAttributeKind
getPropertyAttrs(PropsTy & props) const359*67e74705SXin Li   getPropertyAttrs(PropsTy &props) const {
360*67e74705SXin Li     assert(!props.empty());
361*67e74705SXin Li     ObjCPropertyDecl::PropertyAttributeKind
362*67e74705SXin Li       attrs = props[0].PropD->getPropertyAttributesAsWritten();
363*67e74705SXin Li 
364*67e74705SXin Li #ifndef NDEBUG
365*67e74705SXin Li     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
366*67e74705SXin Li       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
367*67e74705SXin Li #endif
368*67e74705SXin Li 
369*67e74705SXin Li     return attrs;
370*67e74705SXin Li   }
371*67e74705SXin Li };
372*67e74705SXin Li 
373*67e74705SXin Li } // anonymous namespace
374*67e74705SXin Li 
traverseObjCImplementation(ObjCImplementationContext & ImplCtx)375*67e74705SXin Li void PropertyRewriteTraverser::traverseObjCImplementation(
376*67e74705SXin Li                                            ObjCImplementationContext &ImplCtx) {
377*67e74705SXin Li   PropertiesRewriter(ImplCtx.getMigrationContext())
378*67e74705SXin Li                                   .doTransform(ImplCtx.getImplementationDecl());
379*67e74705SXin Li }
380