TransProperties.cpp revision 5f9e272e632e951b1efe824cd16acb4d96077930
1//===--- TransProperties.cpp - Tranformations to ARC mode -----------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// rewriteProperties: 11// 12// - Adds strong/weak/unsafe_unretained ownership specifier to properties that 13// are missing one. 14// - Migrates properties from (retain) to (strong) and (assign) to 15// (unsafe_unretained/weak). 16// - If a property is synthesized, adds the ownership specifier in the ivar 17// backing the property. 18// 19// @interface Foo : NSObject { 20// NSObject *x; 21// } 22// @property (assign) id x; 23// @end 24// ----> 25// @interface Foo : NSObject { 26// NSObject *__weak x; 27// } 28// @property (weak) id x; 29// @end 30// 31//===----------------------------------------------------------------------===// 32 33#include "Transforms.h" 34#include "Internals.h" 35#include "clang/Sema/SemaDiagnostic.h" 36#include "clang/Basic/SourceManager.h" 37#include "clang/Lex/Lexer.h" 38#include <map> 39 40using namespace clang; 41using namespace arcmt; 42using namespace trans; 43 44namespace { 45 46class PropertiesRewriter { 47 MigrationPass &Pass; 48 49 struct PropData { 50 ObjCPropertyDecl *PropD; 51 ObjCIvarDecl *IvarD; 52 ObjCPropertyImplDecl *ImplD; 53 54 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } 55 }; 56 57 typedef SmallVector<PropData, 2> PropsTy; 58 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 59 AtPropDeclsTy AtProps; 60 61public: 62 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } 63 64 void doTransform(ObjCImplementationDecl *D) { 65 ObjCInterfaceDecl *iface = D->getClassInterface(); 66 if (!iface) 67 return; 68 69 for (ObjCInterfaceDecl::prop_iterator 70 propI = iface->prop_begin(), 71 propE = iface->prop_end(); propI != propE; ++propI) { 72 if (propI->getAtLoc().isInvalid()) 73 continue; 74 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; 75 props.push_back(*propI); 76 } 77 78 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 79 prop_impl_iterator; 80 for (prop_impl_iterator 81 I = prop_impl_iterator(D->decls_begin()), 82 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 83 ObjCPropertyImplDecl *implD = *I; 84 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 85 continue; 86 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 87 if (!propD || propD->isInvalidDecl()) 88 continue; 89 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 90 if (!ivarD || ivarD->isInvalidDecl()) 91 continue; 92 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 93 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 94 if (findAtLoc == AtProps.end()) 95 continue; 96 97 PropsTy &props = findAtLoc->second; 98 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 99 if (I->PropD == propD) { 100 I->IvarD = ivarD; 101 I->ImplD = implD; 102 break; 103 } 104 } 105 } 106 107 for (AtPropDeclsTy::iterator 108 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 109 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 110 PropsTy &props = I->second; 111 QualType ty = getPropertyType(props); 112 if (!ty->isObjCRetainableType()) 113 continue; 114 if (hasIvarWithExplicitOwnership(props)) 115 continue; 116 117 Transaction Trans(Pass.TA); 118 rewriteProperty(props, atLoc); 119 } 120 } 121 122private: 123 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const { 124 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 125 126 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 127 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 128 ObjCPropertyDecl::OBJC_PR_strong | 129 ObjCPropertyDecl::OBJC_PR_weak)) 130 return; 131 132 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 133 rewriteAttribute("retain", "strong", atLoc); 134 return; 135 } 136 137 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) 138 return rewriteAssign(props, atLoc); 139 140 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 141 } 142 143 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 144 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 145 146 bool rewroteAttr = rewriteAttribute("assign", 147 canUseWeak ? "weak" : "unsafe_unretained", 148 atLoc); 149 if (!rewroteAttr) 150 canUseWeak = false; 151 152 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 153 if (isUserDeclared(I->IvarD)) 154 Pass.TA.insert(I->IvarD->getLocation(), 155 canUseWeak ? "__weak " : "__unsafe_unretained "); 156 if (I->ImplD) 157 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 158 I->ImplD->getLocation()); 159 } 160 } 161 162 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 163 SourceLocation atLoc) const { 164 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 165 if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) && 166 hasNoBackingIvars(props)) 167 return; 168 169 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 170 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 171 atLoc); 172 if (!addedAttr) 173 canUseWeak = false; 174 175 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 176 if (isUserDeclared(I->IvarD)) 177 Pass.TA.insert(I->IvarD->getLocation(), 178 canUseWeak ? "__weak " : "__unsafe_unretained "); 179 if (I->ImplD) { 180 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 181 I->ImplD->getLocation()); 182 Pass.TA.clearDiagnostic( 183 diag::err_arc_objc_property_default_assign_on_object, 184 I->ImplD->getLocation()); 185 } 186 } 187 } 188 189 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 190 SourceLocation atLoc) const { 191 if (atLoc.isMacroID()) 192 return false; 193 194 SourceManager &SM = Pass.Ctx.getSourceManager(); 195 196 // Break down the source location. 197 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 198 199 // Try to load the file buffer. 200 bool invalidTemp = false; 201 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 202 if (invalidTemp) 203 return false; 204 205 const char *tokenBegin = file.data() + locInfo.second; 206 207 // Lex from the start of the given location. 208 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 209 Pass.Ctx.getLangOptions(), 210 file.begin(), tokenBegin, file.end()); 211 Token tok; 212 lexer.LexFromRawLexer(tok); 213 if (tok.isNot(tok::at)) return false; 214 lexer.LexFromRawLexer(tok); 215 if (tok.isNot(tok::raw_identifier)) return false; 216 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 217 != "property") 218 return false; 219 lexer.LexFromRawLexer(tok); 220 if (tok.isNot(tok::l_paren)) return false; 221 222 lexer.LexFromRawLexer(tok); 223 if (tok.is(tok::r_paren)) 224 return false; 225 226 while (1) { 227 if (tok.isNot(tok::raw_identifier)) return false; 228 StringRef ident(tok.getRawIdentifierData(), tok.getLength()); 229 if (ident == fromAttr) { 230 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); 231 return true; 232 } 233 234 do { 235 lexer.LexFromRawLexer(tok); 236 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); 237 if (tok.is(tok::r_paren)) 238 break; 239 lexer.LexFromRawLexer(tok); 240 } 241 242 return false; 243 } 244 245 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 246 if (atLoc.isMacroID()) 247 return false; 248 249 SourceManager &SM = Pass.Ctx.getSourceManager(); 250 251 // Break down the source location. 252 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 253 254 // Try to load the file buffer. 255 bool invalidTemp = false; 256 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 257 if (invalidTemp) 258 return false; 259 260 const char *tokenBegin = file.data() + locInfo.second; 261 262 // Lex from the start of the given location. 263 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 264 Pass.Ctx.getLangOptions(), 265 file.begin(), tokenBegin, file.end()); 266 Token tok; 267 lexer.LexFromRawLexer(tok); 268 if (tok.isNot(tok::at)) return false; 269 lexer.LexFromRawLexer(tok); 270 if (tok.isNot(tok::raw_identifier)) return false; 271 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 272 != "property") 273 return false; 274 lexer.LexFromRawLexer(tok); 275 276 if (tok.isNot(tok::l_paren)) { 277 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); 278 return true; 279 } 280 281 lexer.LexFromRawLexer(tok); 282 if (tok.is(tok::r_paren)) { 283 Pass.TA.insert(tok.getLocation(), attr); 284 return true; 285 } 286 287 if (tok.isNot(tok::raw_identifier)) return false; 288 289 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); 290 return true; 291 } 292 293 bool hasIvarWithExplicitOwnership(PropsTy &props) const { 294 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 295 if (isUserDeclared(I->IvarD)) { 296 if (isa<AttributedType>(I->IvarD->getType())) 297 return true; 298 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 299 != Qualifiers::OCL_Strong) 300 return true; 301 } 302 } 303 304 return false; 305 } 306 307 bool hasNoBackingIvars(PropsTy &props) const { 308 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 309 if (I->IvarD) 310 return false; 311 312 return true; 313 } 314 315 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 316 return ivarD && !ivarD->getSynthesize(); 317 } 318 319 QualType getPropertyType(PropsTy &props) const { 320 assert(!props.empty()); 321 QualType ty = props[0].PropD->getType(); 322 323#ifndef NDEBUG 324 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 325 assert(ty == I->PropD->getType()); 326#endif 327 328 return ty; 329 } 330 331 ObjCPropertyDecl::PropertyAttributeKind 332 getPropertyAttrs(PropsTy &props) const { 333 assert(!props.empty()); 334 ObjCPropertyDecl::PropertyAttributeKind 335 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 336 337#ifndef NDEBUG 338 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 339 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 340#endif 341 342 return attrs; 343 } 344}; 345 346class ImplementationChecker : 347 public RecursiveASTVisitor<ImplementationChecker> { 348 MigrationPass &Pass; 349 350public: 351 ImplementationChecker(MigrationPass &pass) : Pass(pass) { } 352 353 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { 354 PropertiesRewriter(Pass).doTransform(D); 355 return true; 356 } 357}; 358 359} // anonymous namespace 360 361void trans::rewriteProperties(MigrationPass &pass) { 362 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 363} 364