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