diff options
author | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-07-13 19:22:00 +0000 |
---|---|---|
committer | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-07-13 19:22:00 +0000 |
commit | 14c4b4405fdbb54445c2d2d6320ed4f9e2326696 (patch) | |
tree | e04699f1c68086d465449e5584da342137c7c34f /lib/ARCMigrate/TransProperties.cpp | |
parent | 4c91d89453c4a540d9ad697ceae89c8ef6049a6b (diff) |
[arcmt] For properties rewrite 'assign' -> 'weak or unsafe_unretained', 'retain' -> 'strong', and add
'weak or unsafe_unretained' when 'assign' is missing. rdar://9496219&9602589.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135065 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/ARCMigrate/TransProperties.cpp')
-rw-r--r-- | lib/ARCMigrate/TransProperties.cpp | 370 |
1 files changed, 237 insertions, 133 deletions
diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index 82ef717f31..3a334f9432 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -7,11 +7,14 @@ // //===----------------------------------------------------------------------===// // -// changeIvarsOfAssignProperties: +// rewriteProperties: // -// If a property is synthesized with 'assign' attribute and the user didn't -// set a lifetime attribute, change the property to 'weak' or add -// __unsafe_unretained if the ARC runtime is not available. +// - 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; @@ -32,6 +35,7 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" +#include <map> using namespace clang; using namespace arcmt; @@ -40,35 +44,36 @@ using llvm::StringRef; namespace { -class AssignPropertiesTrans { +class PropertiesRewriter { MigrationPass &Pass; + struct PropData { ObjCPropertyDecl *PropD; ObjCIvarDecl *IvarD; - bool ShouldChangeToWeak; - SourceLocation ArcPropAssignErrorLoc; + ObjCPropertyImplDecl *ImplD; + + PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } }; - typedef llvm::SmallVector<PropData, 2> PropsTy; - typedef llvm::DenseMap<unsigned, PropsTy> PropsMapTy; - PropsMapTy PropsMap; + typedef llvm::SmallVector<PropData, 2> PropsTy; + typedef std::map<unsigned, PropsTy> AtPropDeclsTy; + AtPropDeclsTy AtProps; public: - AssignPropertiesTrans(MigrationPass &pass) : Pass(pass) { } + PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } void doTransform(ObjCImplementationDecl *D) { - SourceManager &SM = Pass.Ctx.getSourceManager(); + ObjCInterfaceDecl *iface = D->getClassInterface(); + if (!iface) + return; - ObjCInterfaceDecl *IFace = D->getClassInterface(); for (ObjCInterfaceDecl::prop_iterator - I = IFace->prop_begin(), E = IFace->prop_end(); I != E; ++I) { - ObjCPropertyDecl *propD = *I; - unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding(); - PropsTy &props = PropsMap[loc]; - props.push_back(PropData()); - props.back().PropD = propD; - props.back().IvarD = 0; - props.back().ShouldChangeToWeak = false; + propI = iface->prop_begin(), + propE = iface->prop_end(); propI != propE; ++propI) { + if (propI->getAtLoc().isInvalid()) + continue; + PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; + props.push_back(*propI); } typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> @@ -76,111 +81,172 @@ public: for (prop_impl_iterator I = prop_impl_iterator(D->decls_begin()), E = prop_impl_iterator(D->decls_end()); I != E; ++I) { - VisitObjCPropertyImplDecl(*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 (PropsMapTy::iterator - I = PropsMap.begin(), E = PropsMap.end(); I != E; ++I) { + for (AtPropDeclsTy::iterator + I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); PropsTy &props = I->second; - if (shouldApplyWeakToAllProp(props)) { - if (changeAssignToWeak(atLoc)) { - // Couldn't add the 'weak' property attribute, - // try adding __unsafe_unretained. - applyUnsafeUnretained(props); - } else { - for (PropsTy::iterator - PI = props.begin(), PE = props.end(); PI != PE; ++PI) { - applyWeak(*PI); - } - } - } else { - // We should not add 'weak' attribute since not all properties need it. - // So just add __unsafe_unretained to the ivars. - applyUnsafeUnretained(props); - } + QualType ty = getPropertyType(props); + if (!ty->isObjCRetainableType()) + continue; + if (hasIvarWithExplicitOwnership(props)) + continue; + + Transaction Trans(Pass.TA); + rewriteProperty(props, atLoc); } } - bool shouldApplyWeakToAllProp(PropsTy &props) { - for (PropsTy::iterator - PI = props.begin(), PE = props.end(); PI != PE; ++PI) { - if (!PI->ShouldChangeToWeak) - return false; +private: + void rewriteProperty(PropsTy &props, SourceLocation atLoc) const { + 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) { + rewriteAttribute("retain", "strong", atLoc); + return; } - return true; + + if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) + return rewriteAssign(props, atLoc); + + return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); } - void applyWeak(PropData &prop) { - assert(canApplyWeak(Pass.Ctx, prop.IvarD->getType())); + void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); + + bool rewroteAttr = rewriteAttribute("assign", + canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!rewroteAttr) + canUseWeak = false; - Transaction Trans(Pass.TA); - Pass.TA.insert(prop.IvarD->getLocation(), "__weak "); - Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, - prop.ArcPropAssignErrorLoc); + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + if (I->ImplD) + Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, + I->ImplD->getLocation()); + } } - void applyUnsafeUnretained(PropsTy &props) { - for (PropsTy::iterator - PI = props.begin(), PE = props.end(); PI != PE; ++PI) { - if (PI->ShouldChangeToWeak) { - Transaction Trans(Pass.TA); - Pass.TA.insert(PI->IvarD->getLocation(), "__unsafe_unretained "); + void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, + SourceLocation atLoc) const { + ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) && + hasNoBackingIvars(props)) + return; + + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); + 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)) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + if (I->ImplD) { Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, - PI->ArcPropAssignErrorLoc); + I->ImplD->getLocation()); + Pass.TA.clearDiagnostic( + diag::err_arc_objc_property_default_assign_on_object, + I->ImplD->getLocation()); } } } - bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { + bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr, + SourceLocation atLoc) const { + if (atLoc.isMacroID()) + return false; + SourceManager &SM = Pass.Ctx.getSourceManager(); - if (D->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) - return true; - ObjCPropertyDecl *propD = D->getPropertyDecl(); - if (!propD || propD->isInvalidDecl()) - return true; - ObjCIvarDecl *ivarD = D->getPropertyIvarDecl(); - if (!ivarD || ivarD->isInvalidDecl()) - return true; - if (!(propD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_assign)) - return true; - if (isa<AttributedType>(ivarD->getType().getTypePtr())) - return true; - if (ivarD->getType().getLocalQualifiers().getObjCLifetime() - != Qualifiers::OCL_Strong) - return true; - if (!Pass.TA.hasDiagnostic( - diag::err_arc_assign_property_ownership, D->getLocation())) - return true; + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); + + // Try to load the file buffer. + bool invalidTemp = false; + llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; - // There is a "error: existing ivar for assign property must be - // __unsafe_unretained"; fix it. + const char *tokenBegin = file.data() + locInfo.second; - if (!canApplyWeak(Pass.Ctx, ivarD->getType())) { - // We will just add __unsafe_unretained to the ivar. - Transaction Trans(Pass.TA); - Pass.TA.insert(ivarD->getLocation(), "__unsafe_unretained "); - Pass.TA.clearDiagnostic( - diag::err_arc_assign_property_ownership, D->getLocation()); - } else { - // Mark that we want the ivar to become weak. - unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding(); - PropsTy &props = PropsMap[loc]; - for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { - if (I->PropD == propD) { - I->IvarD = ivarD; - I->ShouldChangeToWeak = true; - I->ArcPropAssignErrorLoc = D->getLocation(); - } + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Pass.Ctx.getLangOptions(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::at)) return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::raw_identifier)) return false; + if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) + != "property") + return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::l_paren)) return false; + + lexer.LexFromRawLexer(tok); + if (tok.is(tok::r_paren)) + return false; + + while (1) { + if (tok.isNot(tok::raw_identifier)) return false; + llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength()); + if (ident == fromAttr) { + Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); + return true; } + + do { + lexer.LexFromRawLexer(tok); + } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); + if (tok.is(tok::r_paren)) + break; + lexer.LexFromRawLexer(tok); } - return true; + return false; } -private: - bool changeAssignToWeak(SourceLocation atLoc) { + bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const { + if (atLoc.isMacroID()) + return false; + SourceManager &SM = Pass.Ctx.getSourceManager(); // Break down the source location. @@ -190,7 +256,7 @@ private: bool invalidTemp = false; llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); if (invalidTemp) - return true; + return false; const char *tokenBegin = file.data() + locInfo.second; @@ -200,61 +266,99 @@ private: file.begin(), tokenBegin, file.end()); Token tok; lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::at)) return true; + if (tok.isNot(tok::at)) return false; lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::raw_identifier)) return true; + if (tok.isNot(tok::raw_identifier)) return false; if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) != "property") - return true; + return false; lexer.LexFromRawLexer(tok); - if (tok.isNot(tok::l_paren)) return true; - - SourceLocation LParen = tok.getLocation(); - SourceLocation assignLoc; - bool isEmpty = false; + if (tok.isNot(tok::l_paren)) { + Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); + return true; + } + lexer.LexFromRawLexer(tok); if (tok.is(tok::r_paren)) { - isEmpty = true; - } else { - while (1) { - if (tok.isNot(tok::raw_identifier)) return true; - llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength()); - if (ident == "assign") - assignLoc = tok.getLocation(); - - do { - lexer.LexFromRawLexer(tok); - } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); - if (tok.is(tok::r_paren)) - break; - lexer.LexFromRawLexer(tok); + Pass.TA.insert(tok.getLocation(), attr); + return true; + } + + if (tok.isNot(tok::raw_identifier)) return false; + + Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); + return true; + } + + bool hasIvarWithExplicitOwnership(PropsTy &props) const { + 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; } } - Transaction Trans(Pass.TA); - if (assignLoc.isValid()) - Pass.TA.replaceText(assignLoc, "assign", "weak"); - else - Pass.TA.insertAfterToken(LParen, isEmpty ? "weak" : "weak, "); - return false; + return false; + } + + bool hasNoBackingIvars(PropsTy &props) const { + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + if (isUserDeclared(I->IvarD)) + return false; + + return true; + } + + bool isUserDeclared(ObjCIvarDecl *ivarD) const { + return ivarD && !ivarD->getSynthesize(); + } + + QualType getPropertyType(PropsTy &props) const { + assert(!props.empty()); + QualType ty = props[0].PropD->getType(); + +#ifndef NDEBUG + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + assert(ty == I->PropD->getType()); +#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; } }; -class PropertiesChecker : public RecursiveASTVisitor<PropertiesChecker> { +class ImplementationChecker : + public RecursiveASTVisitor<ImplementationChecker> { MigrationPass &Pass; public: - PropertiesChecker(MigrationPass &pass) : Pass(pass) { } + ImplementationChecker(MigrationPass &pass) : Pass(pass) { } bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { - AssignPropertiesTrans(Pass).doTransform(D); + PropertiesRewriter(Pass).doTransform(D); return true; } }; } // anonymous namespace -void trans::changeIvarsOfAssignProperties(MigrationPass &pass) { - PropertiesChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +void trans::rewriteProperties(MigrationPass &pass) { + ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); } |