xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/ARCMigrate/TransProperties.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // rewriteProperties:
107330f729Sjoerg //
117330f729Sjoerg // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
127330f729Sjoerg //   are missing one.
137330f729Sjoerg // - Migrates properties from (retain) to (strong) and (assign) to
147330f729Sjoerg //   (unsafe_unretained/weak).
157330f729Sjoerg // - If a property is synthesized, adds the ownership specifier in the ivar
167330f729Sjoerg //   backing the property.
177330f729Sjoerg //
187330f729Sjoerg //  @interface Foo : NSObject {
197330f729Sjoerg //      NSObject *x;
207330f729Sjoerg //  }
217330f729Sjoerg //  @property (assign) id x;
227330f729Sjoerg //  @end
237330f729Sjoerg // ---->
247330f729Sjoerg //  @interface Foo : NSObject {
257330f729Sjoerg //      NSObject *__weak x;
267330f729Sjoerg //  }
277330f729Sjoerg //  @property (weak) id x;
287330f729Sjoerg //  @end
297330f729Sjoerg //
307330f729Sjoerg //===----------------------------------------------------------------------===//
317330f729Sjoerg 
327330f729Sjoerg #include "Transforms.h"
337330f729Sjoerg #include "Internals.h"
347330f729Sjoerg #include "clang/Basic/SourceManager.h"
357330f729Sjoerg #include "clang/Lex/Lexer.h"
367330f729Sjoerg #include "clang/Sema/SemaDiagnostic.h"
377330f729Sjoerg #include <map>
387330f729Sjoerg 
397330f729Sjoerg using namespace clang;
407330f729Sjoerg using namespace arcmt;
417330f729Sjoerg using namespace trans;
427330f729Sjoerg 
437330f729Sjoerg namespace {
447330f729Sjoerg 
457330f729Sjoerg class PropertiesRewriter {
467330f729Sjoerg   MigrationContext &MigrateCtx;
477330f729Sjoerg   MigrationPass &Pass;
487330f729Sjoerg   ObjCImplementationDecl *CurImplD;
497330f729Sjoerg 
507330f729Sjoerg   enum PropActionKind {
517330f729Sjoerg     PropAction_None,
527330f729Sjoerg     PropAction_RetainReplacedWithStrong,
537330f729Sjoerg     PropAction_AssignRemoved,
547330f729Sjoerg     PropAction_AssignRewritten,
557330f729Sjoerg     PropAction_MaybeAddWeakOrUnsafe
567330f729Sjoerg   };
577330f729Sjoerg 
587330f729Sjoerg   struct PropData {
597330f729Sjoerg     ObjCPropertyDecl *PropD;
607330f729Sjoerg     ObjCIvarDecl *IvarD;
617330f729Sjoerg     ObjCPropertyImplDecl *ImplD;
627330f729Sjoerg 
PropData__anoncea033e80111::PropertiesRewriter::PropData637330f729Sjoerg     PropData(ObjCPropertyDecl *propD)
647330f729Sjoerg       : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
657330f729Sjoerg   };
667330f729Sjoerg 
677330f729Sjoerg   typedef SmallVector<PropData, 2> PropsTy;
68*e038c9c4Sjoerg   typedef std::map<SourceLocation, PropsTy> AtPropDeclsTy;
697330f729Sjoerg   AtPropDeclsTy AtProps;
707330f729Sjoerg   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
717330f729Sjoerg 
727330f729Sjoerg public:
PropertiesRewriter(MigrationContext & MigrateCtx)737330f729Sjoerg   explicit PropertiesRewriter(MigrationContext &MigrateCtx)
747330f729Sjoerg     : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
757330f729Sjoerg 
collectProperties(ObjCContainerDecl * D,AtPropDeclsTy & AtProps,AtPropDeclsTy * PrevAtProps=nullptr)767330f729Sjoerg   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
777330f729Sjoerg                                 AtPropDeclsTy *PrevAtProps = nullptr) {
787330f729Sjoerg     for (auto *Prop : D->instance_properties()) {
79*e038c9c4Sjoerg       SourceLocation Loc = Prop->getAtLoc();
80*e038c9c4Sjoerg       if (Loc.isInvalid())
817330f729Sjoerg         continue;
827330f729Sjoerg       if (PrevAtProps)
83*e038c9c4Sjoerg         if (PrevAtProps->find(Loc) != PrevAtProps->end())
847330f729Sjoerg           continue;
85*e038c9c4Sjoerg       PropsTy &props = AtProps[Loc];
867330f729Sjoerg       props.push_back(Prop);
877330f729Sjoerg     }
887330f729Sjoerg   }
897330f729Sjoerg 
doTransform(ObjCImplementationDecl * D)907330f729Sjoerg   void doTransform(ObjCImplementationDecl *D) {
917330f729Sjoerg     CurImplD = D;
927330f729Sjoerg     ObjCInterfaceDecl *iface = D->getClassInterface();
937330f729Sjoerg     if (!iface)
947330f729Sjoerg       return;
957330f729Sjoerg 
967330f729Sjoerg     collectProperties(iface, AtProps);
977330f729Sjoerg 
987330f729Sjoerg     // Look through extensions.
997330f729Sjoerg     for (auto *Ext : iface->visible_extensions())
1007330f729Sjoerg       collectProperties(Ext, AtProps);
1017330f729Sjoerg 
1027330f729Sjoerg     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
1037330f729Sjoerg         prop_impl_iterator;
1047330f729Sjoerg     for (prop_impl_iterator
1057330f729Sjoerg            I = prop_impl_iterator(D->decls_begin()),
1067330f729Sjoerg            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
1077330f729Sjoerg       ObjCPropertyImplDecl *implD = *I;
1087330f729Sjoerg       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
1097330f729Sjoerg         continue;
1107330f729Sjoerg       ObjCPropertyDecl *propD = implD->getPropertyDecl();
1117330f729Sjoerg       if (!propD || propD->isInvalidDecl())
1127330f729Sjoerg         continue;
1137330f729Sjoerg       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
1147330f729Sjoerg       if (!ivarD || ivarD->isInvalidDecl())
1157330f729Sjoerg         continue;
116*e038c9c4Sjoerg       AtPropDeclsTy::iterator findAtLoc = AtProps.find(propD->getAtLoc());
1177330f729Sjoerg       if (findAtLoc == AtProps.end())
1187330f729Sjoerg         continue;
1197330f729Sjoerg 
1207330f729Sjoerg       PropsTy &props = findAtLoc->second;
1217330f729Sjoerg       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
1227330f729Sjoerg         if (I->PropD == propD) {
1237330f729Sjoerg           I->IvarD = ivarD;
1247330f729Sjoerg           I->ImplD = implD;
1257330f729Sjoerg           break;
1267330f729Sjoerg         }
1277330f729Sjoerg       }
1287330f729Sjoerg     }
1297330f729Sjoerg 
1307330f729Sjoerg     for (AtPropDeclsTy::iterator
1317330f729Sjoerg            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
132*e038c9c4Sjoerg       SourceLocation atLoc = I->first;
1337330f729Sjoerg       PropsTy &props = I->second;
1347330f729Sjoerg       if (!getPropertyType(props)->isObjCRetainableType())
1357330f729Sjoerg         continue;
1367330f729Sjoerg       if (hasIvarWithExplicitARCOwnership(props))
1377330f729Sjoerg         continue;
1387330f729Sjoerg 
1397330f729Sjoerg       Transaction Trans(Pass.TA);
1407330f729Sjoerg       rewriteProperty(props, atLoc);
1417330f729Sjoerg     }
1427330f729Sjoerg   }
1437330f729Sjoerg 
1447330f729Sjoerg private:
doPropAction(PropActionKind kind,PropsTy & props,SourceLocation atLoc,bool markAction=true)1457330f729Sjoerg   void doPropAction(PropActionKind kind,
1467330f729Sjoerg                     PropsTy &props, SourceLocation atLoc,
1477330f729Sjoerg                     bool markAction = true) {
1487330f729Sjoerg     if (markAction)
1497330f729Sjoerg       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
1507330f729Sjoerg         ActionOnProp[I->PropD->getIdentifier()] = kind;
1517330f729Sjoerg 
1527330f729Sjoerg     switch (kind) {
1537330f729Sjoerg     case PropAction_None:
1547330f729Sjoerg       return;
1557330f729Sjoerg     case PropAction_RetainReplacedWithStrong: {
1567330f729Sjoerg       StringRef toAttr = "strong";
1577330f729Sjoerg       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
1587330f729Sjoerg       return;
1597330f729Sjoerg     }
1607330f729Sjoerg     case PropAction_AssignRemoved:
1617330f729Sjoerg       return removeAssignForDefaultStrong(props, atLoc);
1627330f729Sjoerg     case PropAction_AssignRewritten:
1637330f729Sjoerg       return rewriteAssign(props, atLoc);
1647330f729Sjoerg     case PropAction_MaybeAddWeakOrUnsafe:
1657330f729Sjoerg       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
1667330f729Sjoerg     }
1677330f729Sjoerg   }
1687330f729Sjoerg 
rewriteProperty(PropsTy & props,SourceLocation atLoc)1697330f729Sjoerg   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
170*e038c9c4Sjoerg     ObjCPropertyAttribute::Kind propAttrs = getPropertyAttrs(props);
1717330f729Sjoerg 
172*e038c9c4Sjoerg     if (propAttrs &
173*e038c9c4Sjoerg         (ObjCPropertyAttribute::kind_copy |
174*e038c9c4Sjoerg          ObjCPropertyAttribute::kind_unsafe_unretained |
175*e038c9c4Sjoerg          ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak))
1767330f729Sjoerg       return;
1777330f729Sjoerg 
178*e038c9c4Sjoerg     if (propAttrs & ObjCPropertyAttribute::kind_retain) {
1797330f729Sjoerg       // strong is the default.
1807330f729Sjoerg       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
1817330f729Sjoerg     }
1827330f729Sjoerg 
1837330f729Sjoerg     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
1847330f729Sjoerg 
185*e038c9c4Sjoerg     if (propAttrs & ObjCPropertyAttribute::kind_assign) {
1867330f729Sjoerg       if (HasIvarAssignedAPlusOneObject)
1877330f729Sjoerg         return doPropAction(PropAction_AssignRemoved, props, atLoc);
1887330f729Sjoerg       return doPropAction(PropAction_AssignRewritten, props, atLoc);
1897330f729Sjoerg     }
1907330f729Sjoerg 
1917330f729Sjoerg     if (HasIvarAssignedAPlusOneObject ||
1927330f729Sjoerg         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
1937330f729Sjoerg       return; // 'strong' by default.
1947330f729Sjoerg 
1957330f729Sjoerg     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
1967330f729Sjoerg   }
1977330f729Sjoerg 
removeAssignForDefaultStrong(PropsTy & props,SourceLocation atLoc) const1987330f729Sjoerg   void removeAssignForDefaultStrong(PropsTy &props,
1997330f729Sjoerg                                     SourceLocation atLoc) const {
2007330f729Sjoerg     removeAttribute("retain", atLoc);
2017330f729Sjoerg     if (!removeAttribute("assign", atLoc))
2027330f729Sjoerg       return;
2037330f729Sjoerg 
2047330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
2057330f729Sjoerg       if (I->ImplD)
2067330f729Sjoerg         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
2077330f729Sjoerg                                 diag::err_arc_assign_property_ownership,
2087330f729Sjoerg                                 diag::err_arc_inconsistent_property_ownership,
2097330f729Sjoerg                                 I->IvarD->getLocation());
2107330f729Sjoerg     }
2117330f729Sjoerg   }
2127330f729Sjoerg 
rewriteAssign(PropsTy & props,SourceLocation atLoc) const2137330f729Sjoerg   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
2147330f729Sjoerg     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
2157330f729Sjoerg                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
2167330f729Sjoerg     const char *toWhich =
2177330f729Sjoerg       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
2187330f729Sjoerg       (canUseWeak ? "weak" : "unsafe_unretained");
2197330f729Sjoerg 
2207330f729Sjoerg     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
2217330f729Sjoerg     if (!rewroteAttr)
2227330f729Sjoerg       canUseWeak = false;
2237330f729Sjoerg 
2247330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
2257330f729Sjoerg       if (isUserDeclared(I->IvarD)) {
2267330f729Sjoerg         if (I->IvarD &&
2277330f729Sjoerg             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
2287330f729Sjoerg           const char *toWhich =
2297330f729Sjoerg             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
2307330f729Sjoerg               (canUseWeak ? "__weak " : "__unsafe_unretained ");
2317330f729Sjoerg           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
2327330f729Sjoerg         }
2337330f729Sjoerg       }
2347330f729Sjoerg       if (I->ImplD)
2357330f729Sjoerg         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
2367330f729Sjoerg                                 diag::err_arc_assign_property_ownership,
2377330f729Sjoerg                                 diag::err_arc_inconsistent_property_ownership,
2387330f729Sjoerg                                 I->IvarD->getLocation());
2397330f729Sjoerg     }
2407330f729Sjoerg   }
2417330f729Sjoerg 
maybeAddWeakOrUnsafeUnretainedAttr(PropsTy & props,SourceLocation atLoc) const2427330f729Sjoerg   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
2437330f729Sjoerg                                           SourceLocation atLoc) const {
2447330f729Sjoerg     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
2457330f729Sjoerg                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
2467330f729Sjoerg 
2477330f729Sjoerg     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
2487330f729Sjoerg                                   atLoc);
2497330f729Sjoerg     if (!addedAttr)
2507330f729Sjoerg       canUseWeak = false;
2517330f729Sjoerg 
2527330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
2537330f729Sjoerg       if (isUserDeclared(I->IvarD)) {
2547330f729Sjoerg         if (I->IvarD &&
2557330f729Sjoerg             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
2567330f729Sjoerg           Pass.TA.insert(I->IvarD->getLocation(),
2577330f729Sjoerg                          canUseWeak ? "__weak " : "__unsafe_unretained ");
2587330f729Sjoerg       }
2597330f729Sjoerg       if (I->ImplD) {
2607330f729Sjoerg         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
2617330f729Sjoerg                                 diag::err_arc_assign_property_ownership,
2627330f729Sjoerg                                 diag::err_arc_inconsistent_property_ownership,
2637330f729Sjoerg                                 I->IvarD->getLocation());
2647330f729Sjoerg         Pass.TA.clearDiagnostic(
2657330f729Sjoerg                            diag::err_arc_objc_property_default_assign_on_object,
2667330f729Sjoerg                            I->ImplD->getLocation());
2677330f729Sjoerg       }
2687330f729Sjoerg     }
2697330f729Sjoerg   }
2707330f729Sjoerg 
removeAttribute(StringRef fromAttr,SourceLocation atLoc) const2717330f729Sjoerg   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
2727330f729Sjoerg     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
2737330f729Sjoerg   }
2747330f729Sjoerg 
rewriteAttribute(StringRef fromAttr,StringRef toAttr,SourceLocation atLoc) const2757330f729Sjoerg   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
2767330f729Sjoerg                         SourceLocation atLoc) const {
2777330f729Sjoerg     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
2787330f729Sjoerg   }
2797330f729Sjoerg 
addAttribute(StringRef attr,SourceLocation atLoc) const2807330f729Sjoerg   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
2817330f729Sjoerg     return MigrateCtx.addPropertyAttribute(attr, atLoc);
2827330f729Sjoerg   }
2837330f729Sjoerg 
2847330f729Sjoerg   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
2857330f729Sjoerg     ObjCIvarDecl *Ivar;
2867330f729Sjoerg   public:
PlusOneAssign(ObjCIvarDecl * D)2877330f729Sjoerg     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
2887330f729Sjoerg 
VisitBinaryOperator(BinaryOperator * E)289*e038c9c4Sjoerg     bool VisitBinaryOperator(BinaryOperator *E) {
290*e038c9c4Sjoerg       if (E->getOpcode() != BO_Assign)
291*e038c9c4Sjoerg         return true;
292*e038c9c4Sjoerg 
2937330f729Sjoerg       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
2947330f729Sjoerg       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
2957330f729Sjoerg         if (RE->getDecl() != Ivar)
2967330f729Sjoerg           return true;
2977330f729Sjoerg 
2987330f729Sjoerg         if (isPlusOneAssign(E))
2997330f729Sjoerg           return false;
3007330f729Sjoerg       }
3017330f729Sjoerg 
3027330f729Sjoerg       return true;
3037330f729Sjoerg     }
3047330f729Sjoerg   };
3057330f729Sjoerg 
hasIvarAssignedAPlusOneObject(PropsTy & props) const3067330f729Sjoerg   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
3077330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
3087330f729Sjoerg       PlusOneAssign oneAssign(I->IvarD);
3097330f729Sjoerg       bool notFound = oneAssign.TraverseDecl(CurImplD);
3107330f729Sjoerg       if (!notFound)
3117330f729Sjoerg         return true;
3127330f729Sjoerg     }
3137330f729Sjoerg 
3147330f729Sjoerg     return false;
3157330f729Sjoerg   }
3167330f729Sjoerg 
hasIvarWithExplicitARCOwnership(PropsTy & props) const3177330f729Sjoerg   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
3187330f729Sjoerg     if (Pass.isGCMigration())
3197330f729Sjoerg       return false;
3207330f729Sjoerg 
3217330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
3227330f729Sjoerg       if (isUserDeclared(I->IvarD)) {
3237330f729Sjoerg         if (isa<AttributedType>(I->IvarD->getType()))
3247330f729Sjoerg           return true;
3257330f729Sjoerg         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
3267330f729Sjoerg               != Qualifiers::OCL_Strong)
3277330f729Sjoerg           return true;
3287330f729Sjoerg       }
3297330f729Sjoerg     }
3307330f729Sjoerg 
3317330f729Sjoerg     return false;
3327330f729Sjoerg   }
3337330f729Sjoerg 
3347330f729Sjoerg   // Returns true if all declarations in the @property have GC __weak.
hasGCWeak(PropsTy & props,SourceLocation atLoc) const3357330f729Sjoerg   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
3367330f729Sjoerg     if (!Pass.isGCMigration())
3377330f729Sjoerg       return false;
3387330f729Sjoerg     if (props.empty())
3397330f729Sjoerg       return false;
340*e038c9c4Sjoerg     return MigrateCtx.AtPropsWeak.count(atLoc);
3417330f729Sjoerg   }
3427330f729Sjoerg 
isUserDeclared(ObjCIvarDecl * ivarD) const3437330f729Sjoerg   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
3447330f729Sjoerg     return ivarD && !ivarD->getSynthesize();
3457330f729Sjoerg   }
3467330f729Sjoerg 
getPropertyType(PropsTy & props) const3477330f729Sjoerg   QualType getPropertyType(PropsTy &props) const {
3487330f729Sjoerg     assert(!props.empty());
3497330f729Sjoerg     QualType ty = props[0].PropD->getType().getUnqualifiedType();
3507330f729Sjoerg 
3517330f729Sjoerg #ifndef NDEBUG
3527330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
3537330f729Sjoerg       assert(ty == I->PropD->getType().getUnqualifiedType());
3547330f729Sjoerg #endif
3557330f729Sjoerg 
3567330f729Sjoerg     return ty;
3577330f729Sjoerg   }
3587330f729Sjoerg 
getPropertyAttrs(PropsTy & props) const359*e038c9c4Sjoerg   ObjCPropertyAttribute::Kind getPropertyAttrs(PropsTy &props) const {
3607330f729Sjoerg     assert(!props.empty());
361*e038c9c4Sjoerg     ObjCPropertyAttribute::Kind attrs =
362*e038c9c4Sjoerg         props[0].PropD->getPropertyAttributesAsWritten();
3637330f729Sjoerg 
3647330f729Sjoerg #ifndef NDEBUG
3657330f729Sjoerg     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
3667330f729Sjoerg       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
3677330f729Sjoerg #endif
3687330f729Sjoerg 
3697330f729Sjoerg     return attrs;
3707330f729Sjoerg   }
3717330f729Sjoerg };
3727330f729Sjoerg 
3737330f729Sjoerg } // anonymous namespace
3747330f729Sjoerg 
traverseObjCImplementation(ObjCImplementationContext & ImplCtx)3757330f729Sjoerg void PropertyRewriteTraverser::traverseObjCImplementation(
3767330f729Sjoerg                                            ObjCImplementationContext &ImplCtx) {
3777330f729Sjoerg   PropertiesRewriter(ImplCtx.getMigrationContext())
3787330f729Sjoerg                                   .doTransform(ImplCtx.getImplementationDecl());
3797330f729Sjoerg }
380