Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransProperties.cpp
35236 views
//===--- TransProperties.cpp - Transformations to ARC mode ----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// rewriteProperties:9//10// - Adds strong/weak/unsafe_unretained ownership specifier to properties that11// are missing one.12// - Migrates properties from (retain) to (strong) and (assign) to13// (unsafe_unretained/weak).14// - If a property is synthesized, adds the ownership specifier in the ivar15// backing the property.16//17// @interface Foo : NSObject {18// NSObject *x;19// }20// @property (assign) id x;21// @end22// ---->23// @interface Foo : NSObject {24// NSObject *__weak x;25// }26// @property (weak) id x;27// @end28//29//===----------------------------------------------------------------------===//3031#include "Transforms.h"32#include "Internals.h"33#include "clang/Basic/SourceManager.h"34#include "clang/Lex/Lexer.h"35#include "clang/Sema/SemaDiagnostic.h"36#include <map>3738using namespace clang;39using namespace arcmt;40using namespace trans;4142namespace {4344class PropertiesRewriter {45MigrationContext &MigrateCtx;46MigrationPass &Pass;47ObjCImplementationDecl *CurImplD = nullptr;4849enum PropActionKind {50PropAction_None,51PropAction_RetainReplacedWithStrong,52PropAction_AssignRemoved,53PropAction_AssignRewritten,54PropAction_MaybeAddWeakOrUnsafe55};5657struct PropData {58ObjCPropertyDecl *PropD;59ObjCIvarDecl *IvarD;60ObjCPropertyImplDecl *ImplD;6162PropData(ObjCPropertyDecl *propD)63: PropD(propD), IvarD(nullptr), ImplD(nullptr) {}64};6566typedef SmallVector<PropData, 2> PropsTy;67typedef std::map<SourceLocation, PropsTy> AtPropDeclsTy;68AtPropDeclsTy AtProps;69llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;7071public:72explicit PropertiesRewriter(MigrationContext &MigrateCtx)73: MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }7475static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,76AtPropDeclsTy *PrevAtProps = nullptr) {77for (auto *Prop : D->instance_properties()) {78SourceLocation Loc = Prop->getAtLoc();79if (Loc.isInvalid())80continue;81if (PrevAtProps)82if (PrevAtProps->find(Loc) != PrevAtProps->end())83continue;84PropsTy &props = AtProps[Loc];85props.push_back(Prop);86}87}8889void doTransform(ObjCImplementationDecl *D) {90CurImplD = D;91ObjCInterfaceDecl *iface = D->getClassInterface();92if (!iface)93return;9495collectProperties(iface, AtProps);9697// Look through extensions.98for (auto *Ext : iface->visible_extensions())99collectProperties(Ext, AtProps);100101typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>102prop_impl_iterator;103for (prop_impl_iterator104I = prop_impl_iterator(D->decls_begin()),105E = prop_impl_iterator(D->decls_end()); I != E; ++I) {106ObjCPropertyImplDecl *implD = *I;107if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)108continue;109ObjCPropertyDecl *propD = implD->getPropertyDecl();110if (!propD || propD->isInvalidDecl())111continue;112ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();113if (!ivarD || ivarD->isInvalidDecl())114continue;115AtPropDeclsTy::iterator findAtLoc = AtProps.find(propD->getAtLoc());116if (findAtLoc == AtProps.end())117continue;118119PropsTy &props = findAtLoc->second;120for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {121if (I->PropD == propD) {122I->IvarD = ivarD;123I->ImplD = implD;124break;125}126}127}128129for (AtPropDeclsTy::iterator130I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {131SourceLocation atLoc = I->first;132PropsTy &props = I->second;133if (!getPropertyType(props)->isObjCRetainableType())134continue;135if (hasIvarWithExplicitARCOwnership(props))136continue;137138Transaction Trans(Pass.TA);139rewriteProperty(props, atLoc);140}141}142143private:144void doPropAction(PropActionKind kind,145PropsTy &props, SourceLocation atLoc,146bool markAction = true) {147if (markAction)148for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)149ActionOnProp[I->PropD->getIdentifier()] = kind;150151switch (kind) {152case PropAction_None:153return;154case PropAction_RetainReplacedWithStrong: {155StringRef toAttr = "strong";156MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);157return;158}159case PropAction_AssignRemoved:160return removeAssignForDefaultStrong(props, atLoc);161case PropAction_AssignRewritten:162return rewriteAssign(props, atLoc);163case PropAction_MaybeAddWeakOrUnsafe:164return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);165}166}167168void rewriteProperty(PropsTy &props, SourceLocation atLoc) {169ObjCPropertyAttribute::Kind propAttrs = getPropertyAttrs(props);170171if (propAttrs &172(ObjCPropertyAttribute::kind_copy |173ObjCPropertyAttribute::kind_unsafe_unretained |174ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak))175return;176177if (propAttrs & ObjCPropertyAttribute::kind_retain) {178// strong is the default.179return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);180}181182bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);183184if (propAttrs & ObjCPropertyAttribute::kind_assign) {185if (HasIvarAssignedAPlusOneObject)186return doPropAction(PropAction_AssignRemoved, props, atLoc);187return doPropAction(PropAction_AssignRewritten, props, atLoc);188}189190if (HasIvarAssignedAPlusOneObject ||191(Pass.isGCMigration() && !hasGCWeak(props, atLoc)))192return; // 'strong' by default.193194return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);195}196197void removeAssignForDefaultStrong(PropsTy &props,198SourceLocation atLoc) const {199removeAttribute("retain", atLoc);200if (!removeAttribute("assign", atLoc))201return;202203for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {204if (I->ImplD)205Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,206diag::err_arc_assign_property_ownership,207diag::err_arc_inconsistent_property_ownership,208I->IvarD->getLocation());209}210}211212void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {213bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),214/*AllowOnUnknownClass=*/Pass.isGCMigration());215const char *toWhich =216(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :217(canUseWeak ? "weak" : "unsafe_unretained");218219bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);220if (!rewroteAttr)221canUseWeak = false;222223for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {224if (isUserDeclared(I->IvarD)) {225if (I->IvarD &&226I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {227const char *toWhich =228(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :229(canUseWeak ? "__weak " : "__unsafe_unretained ");230Pass.TA.insert(I->IvarD->getLocation(), toWhich);231}232}233if (I->ImplD)234Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,235diag::err_arc_assign_property_ownership,236diag::err_arc_inconsistent_property_ownership,237I->IvarD->getLocation());238}239}240241void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,242SourceLocation atLoc) const {243bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),244/*AllowOnUnknownClass=*/Pass.isGCMigration());245246bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",247atLoc);248if (!addedAttr)249canUseWeak = false;250251for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {252if (isUserDeclared(I->IvarD)) {253if (I->IvarD &&254I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)255Pass.TA.insert(I->IvarD->getLocation(),256canUseWeak ? "__weak " : "__unsafe_unretained ");257}258if (I->ImplD) {259Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,260diag::err_arc_assign_property_ownership,261diag::err_arc_inconsistent_property_ownership,262I->IvarD->getLocation());263Pass.TA.clearDiagnostic(264diag::err_arc_objc_property_default_assign_on_object,265I->ImplD->getLocation());266}267}268}269270bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {271return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);272}273274bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,275SourceLocation atLoc) const {276return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);277}278279bool addAttribute(StringRef attr, SourceLocation atLoc) const {280return MigrateCtx.addPropertyAttribute(attr, atLoc);281}282283class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {284ObjCIvarDecl *Ivar;285public:286PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}287288bool VisitBinaryOperator(BinaryOperator *E) {289if (E->getOpcode() != BO_Assign)290return true;291292Expr *lhs = E->getLHS()->IgnoreParenImpCasts();293if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {294if (RE->getDecl() != Ivar)295return true;296297if (isPlusOneAssign(E))298return false;299}300301return true;302}303};304305bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {306for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {307PlusOneAssign oneAssign(I->IvarD);308bool notFound = oneAssign.TraverseDecl(CurImplD);309if (!notFound)310return true;311}312313return false;314}315316bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {317if (Pass.isGCMigration())318return false;319320for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {321if (isUserDeclared(I->IvarD)) {322if (isa<AttributedType>(I->IvarD->getType()))323return true;324if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()325!= Qualifiers::OCL_Strong)326return true;327}328}329330return false;331}332333// Returns true if all declarations in the @property have GC __weak.334bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {335if (!Pass.isGCMigration())336return false;337if (props.empty())338return false;339return MigrateCtx.AtPropsWeak.count(atLoc);340}341342bool isUserDeclared(ObjCIvarDecl *ivarD) const {343return ivarD && !ivarD->getSynthesize();344}345346QualType getPropertyType(PropsTy &props) const {347assert(!props.empty());348QualType ty = props[0].PropD->getType().getUnqualifiedType();349350#ifndef NDEBUG351for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)352assert(ty == I->PropD->getType().getUnqualifiedType());353#endif354355return ty;356}357358ObjCPropertyAttribute::Kind getPropertyAttrs(PropsTy &props) const {359assert(!props.empty());360ObjCPropertyAttribute::Kind attrs =361props[0].PropD->getPropertyAttributesAsWritten();362363#ifndef NDEBUG364for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)365assert(attrs == I->PropD->getPropertyAttributesAsWritten());366#endif367368return attrs;369}370};371372} // anonymous namespace373374void PropertyRewriteTraverser::traverseObjCImplementation(375ObjCImplementationContext &ImplCtx) {376PropertiesRewriter(ImplCtx.getMigrationContext())377.doTransform(ImplCtx.getImplementationDecl());378}379380381