diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp b/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp new file mode 100644 index 000000000000..912f77aeb789 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp @@ -0,0 +1,379 @@ +//===--- TransProperties.cpp - Transformations to ARC mode ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteProperties: +// +// - Adds strong/weak/unsafe_unretained ownership specifier to properties that +// are missing one. +// - Migrates properties from (retain) to (strong) and (assign) to +// (unsafe_unretained/weak). +// - If a property is synthesized, adds the ownership specifier in the ivar +// backing the property. +// +// @interface Foo : NSObject { +// NSObject *x; +// } +// @property (assign) id x; +// @end +// ----> +// @interface Foo : NSObject { +// NSObject *__weak x; +// } +// @property (weak) id x; +// @end +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaDiagnostic.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using namespace trans; + +namespace { + +class PropertiesRewriter { + MigrationContext &MigrateCtx; + MigrationPass &Pass; + ObjCImplementationDecl *CurImplD; + + enum PropActionKind { + PropAction_None, + PropAction_RetainReplacedWithStrong, + PropAction_AssignRemoved, + PropAction_AssignRewritten, + PropAction_MaybeAddWeakOrUnsafe + }; + + struct PropData { + ObjCPropertyDecl *PropD; + ObjCIvarDecl *IvarD; + ObjCPropertyImplDecl *ImplD; + + PropData(ObjCPropertyDecl *propD) + : PropD(propD), IvarD(nullptr), ImplD(nullptr) {} + }; + + typedef SmallVector<PropData, 2> PropsTy; + typedef std::map<unsigned, PropsTy> AtPropDeclsTy; + AtPropDeclsTy AtProps; + llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp; + +public: + explicit PropertiesRewriter(MigrationContext &MigrateCtx) + : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { } + + static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps, + AtPropDeclsTy *PrevAtProps = nullptr) { + for (auto *Prop : D->instance_properties()) { + if (Prop->getAtLoc().isInvalid()) + continue; + unsigned RawLoc = Prop->getAtLoc().getRawEncoding(); + if (PrevAtProps) + if (PrevAtProps->find(RawLoc) != PrevAtProps->end()) + continue; + PropsTy &props = AtProps[RawLoc]; + props.push_back(Prop); + } + } + + void doTransform(ObjCImplementationDecl *D) { + CurImplD = D; + ObjCInterfaceDecl *iface = D->getClassInterface(); + if (!iface) + return; + + collectProperties(iface, AtProps); + + // Look through extensions. + for (auto *Ext : iface->visible_extensions()) + collectProperties(Ext, AtProps); + + typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> + prop_impl_iterator; + for (prop_impl_iterator + I = prop_impl_iterator(D->decls_begin()), + E = prop_impl_iterator(D->decls_end()); I != E; ++I) { + ObjCPropertyImplDecl *implD = *I; + if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + ObjCPropertyDecl *propD = implD->getPropertyDecl(); + if (!propD || propD->isInvalidDecl()) + continue; + ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); + if (!ivarD || ivarD->isInvalidDecl()) + continue; + unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); + AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); + if (findAtLoc == AtProps.end()) + continue; + + PropsTy &props = findAtLoc->second; + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (I->PropD == propD) { + I->IvarD = ivarD; + I->ImplD = implD; + break; + } + } + } + + for (AtPropDeclsTy::iterator + I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { + SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); + PropsTy &props = I->second; + if (!getPropertyType(props)->isObjCRetainableType()) + continue; + if (hasIvarWithExplicitARCOwnership(props)) + continue; + + Transaction Trans(Pass.TA); + rewriteProperty(props, atLoc); + } + } + +private: + void doPropAction(PropActionKind kind, + PropsTy &props, SourceLocation atLoc, + bool markAction = true) { + if (markAction) + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + ActionOnProp[I->PropD->getIdentifier()] = kind; + + switch (kind) { + case PropAction_None: + return; + case PropAction_RetainReplacedWithStrong: { + StringRef toAttr = "strong"; + MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc); + return; + } + case PropAction_AssignRemoved: + return removeAssignForDefaultStrong(props, atLoc); + case PropAction_AssignRewritten: + return rewriteAssign(props, atLoc); + case PropAction_MaybeAddWeakOrUnsafe: + return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); + } + } + + void rewriteProperty(PropsTy &props, SourceLocation atLoc) { + ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + + if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_weak)) + return; + + if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { + // strong is the default. + return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc); + } + + bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props); + + if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { + if (HasIvarAssignedAPlusOneObject) + return doPropAction(PropAction_AssignRemoved, props, atLoc); + return doPropAction(PropAction_AssignRewritten, props, atLoc); + } + + if (HasIvarAssignedAPlusOneObject || + (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) + return; // 'strong' by default. + + return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc); + } + + void removeAssignForDefaultStrong(PropsTy &props, + SourceLocation atLoc) const { + removeAttribute("retain", atLoc); + if (!removeAttribute("assign", atLoc)) + return; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (I->ImplD) + Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, + diag::err_arc_assign_property_ownership, + diag::err_arc_inconsistent_property_ownership, + I->IvarD->getLocation()); + } + } + + void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), + /*AllowOnUnknownClass=*/Pass.isGCMigration()); + const char *toWhich = + (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" : + (canUseWeak ? "weak" : "unsafe_unretained"); + + bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc); + if (!rewroteAttr) + canUseWeak = false; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) { + if (I->IvarD && + I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) { + const char *toWhich = + (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " : + (canUseWeak ? "__weak " : "__unsafe_unretained "); + Pass.TA.insert(I->IvarD->getLocation(), toWhich); + } + } + if (I->ImplD) + Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, + diag::err_arc_assign_property_ownership, + diag::err_arc_inconsistent_property_ownership, + I->IvarD->getLocation()); + } + } + + void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, + SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props), + /*AllowOnUnknownClass=*/Pass.isGCMigration()); + + bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!addedAttr) + canUseWeak = false; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) { + if (I->IvarD && + I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + } + if (I->ImplD) { + Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership, + diag::err_arc_assign_property_ownership, + diag::err_arc_inconsistent_property_ownership, + I->IvarD->getLocation()); + Pass.TA.clearDiagnostic( + diag::err_arc_objc_property_default_assign_on_object, + I->ImplD->getLocation()); + } + } + } + + bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { + return MigrateCtx.removePropertyAttribute(fromAttr, atLoc); + } + + bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, + SourceLocation atLoc) const { + return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc); + } + + bool addAttribute(StringRef attr, SourceLocation atLoc) const { + return MigrateCtx.addPropertyAttribute(attr, atLoc); + } + + class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { + ObjCIvarDecl *Ivar; + public: + PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} + + bool VisitBinAssign(BinaryOperator *E) { + Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); + if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { + if (RE->getDecl() != Ivar) + return true; + + if (isPlusOneAssign(E)) + return false; + } + + return true; + } + }; + + bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + PlusOneAssign oneAssign(I->IvarD); + bool notFound = oneAssign.TraverseDecl(CurImplD); + if (!notFound) + return true; + } + + return false; + } + + bool hasIvarWithExplicitARCOwnership(PropsTy &props) const { + if (Pass.isGCMigration()) + return false; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) { + if (isa<AttributedType>(I->IvarD->getType())) + return true; + if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() + != Qualifiers::OCL_Strong) + return true; + } + } + + return false; + } + + // Returns true if all declarations in the @property have GC __weak. + bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const { + if (!Pass.isGCMigration()) + return false; + if (props.empty()) + return false; + return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding()); + } + + bool isUserDeclared(ObjCIvarDecl *ivarD) const { + return ivarD && !ivarD->getSynthesize(); + } + + QualType getPropertyType(PropsTy &props) const { + assert(!props.empty()); + QualType ty = props[0].PropD->getType().getUnqualifiedType(); + +#ifndef NDEBUG + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + assert(ty == I->PropD->getType().getUnqualifiedType()); +#endif + + return ty; + } + + ObjCPropertyDecl::PropertyAttributeKind + getPropertyAttrs(PropsTy &props) const { + assert(!props.empty()); + ObjCPropertyDecl::PropertyAttributeKind + attrs = props[0].PropD->getPropertyAttributesAsWritten(); + +#ifndef NDEBUG + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + assert(attrs == I->PropD->getPropertyAttributesAsWritten()); +#endif + + return attrs; + } +}; + +} // anonymous namespace + +void PropertyRewriteTraverser::traverseObjCImplementation( + ObjCImplementationContext &ImplCtx) { + PropertiesRewriter(ImplCtx.getMigrationContext()) + .doTransform(ImplCtx.getImplementationDecl()); +} |