//===--- TransProperties.cpp - Tranformations to ARC mode -----------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // changeIvarsOfAssignProperties: // // 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. // // @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/Sema/SemaDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" using namespace clang; using namespace arcmt; using namespace trans; using llvm::StringRef; namespace { class AssignPropertiesTrans { MigrationPass &Pass; struct PropData { ObjCPropertyDecl *PropD; ObjCIvarDecl *IvarD; bool ShouldChangeToWeak; SourceLocation ArcPropAssignErrorLoc; }; typedef llvm::SmallVector PropsTy; typedef llvm::DenseMap PropsMapTy; PropsMapTy PropsMap; public: AssignPropertiesTrans(MigrationPass &pass) : Pass(pass) { } void doTransform(ObjCImplementationDecl *D) { SourceManager &SM = Pass.Ctx.getSourceManager(); 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; } typedef DeclContext::specific_decl_iterator prop_impl_iterator; for (prop_impl_iterator I = prop_impl_iterator(D->decls_begin()), E = prop_impl_iterator(D->decls_end()); I != E; ++I) { VisitObjCPropertyImplDecl(*I); } for (PropsMapTy::iterator I = PropsMap.begin(), E = PropsMap.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); } } } bool shouldApplyWeakToAllProp(PropsTy &props) { for (PropsTy::iterator PI = props.begin(), PE = props.end(); PI != PE; ++PI) { if (!PI->ShouldChangeToWeak) return false; } return true; } void applyWeak(PropData &prop) { assert(!Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime); Transaction Trans(Pass.TA); Pass.TA.insert(prop.IvarD->getLocation(), "__weak "); Pass.TA.clearDiagnostic(diag::err_arc_assign_property_lifetime, prop.ArcPropAssignErrorLoc); } 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 "); Pass.TA.clearDiagnostic(diag::err_arc_assign_property_lifetime, PI->ArcPropAssignErrorLoc); } } } bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { 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(ivarD->getType().getTypePtr())) return true; if (ivarD->getType().getLocalQualifiers().getObjCLifetime() != Qualifiers::OCL_Strong) return true; if (!Pass.TA.hasDiagnostic( diag::err_arc_assign_property_lifetime, D->getLocation())) return true; // There is a "error: existing ivar for assign property must be // __unsafe_unretained"; fix it. if (Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime) { // 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_lifetime, 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(); } } } return true; } private: bool changeAssignToWeak(SourceLocation atLoc) { SourceManager &SM = Pass.Ctx.getSourceManager(); // Break down the source location. std::pair locInfo = SM.getDecomposedLoc(atLoc); // Try to load the file buffer. bool invalidTemp = false; llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); if (invalidTemp) return true; const char *tokenBegin = file.data() + locInfo.second; // 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 true; lexer.LexFromRawLexer(tok); if (tok.isNot(tok::raw_identifier)) return true; if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) != "property") return true; lexer.LexFromRawLexer(tok); if (tok.isNot(tok::l_paren)) return true; SourceLocation LParen = tok.getLocation(); SourceLocation assignLoc; bool isEmpty = false; 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); } } Transaction Trans(Pass.TA); if (assignLoc.isValid()) Pass.TA.replaceText(assignLoc, "assign", "weak"); else Pass.TA.insertAfterToken(LParen, isEmpty ? "weak" : "weak, "); return false; } }; class PropertiesChecker : public RecursiveASTVisitor { MigrationPass &Pass; public: PropertiesChecker(MigrationPass &pass) : Pass(pass) { } bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { AssignPropertiesTrans(Pass).doTransform(D); return true; } }; } // anonymous namespace void trans::changeIvarsOfAssignProperties(MigrationPass &pass) { PropertiesChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); }