TransProperties.cpp 13 KB

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