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 40 using namespace clang; 41 using namespace arcmt; 42 using namespace trans; 43 44 namespace { 45 46 class PropertiesRewriter { 47 MigrationPass &Pass; 48 ObjCImplementationDecl *CurImplD; 49 50 struct PropData { 51 ObjCPropertyDecl *PropD; 52 ObjCIvarDecl *IvarD; 53 ObjCPropertyImplDecl *ImplD; 54 55 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } 56 }; 57 58 typedef SmallVector<PropData, 2> PropsTy; 59 typedef std::map<unsigned, PropsTy> AtPropDeclsTy; 60 AtPropDeclsTy AtProps; 61 62 public: 63 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } 64 65 void doTransform(ObjCImplementationDecl *D) { 66 CurImplD = D; 67 ObjCInterfaceDecl *iface = D->getClassInterface(); 68 if (!iface) 69 return; 70 71 for (ObjCInterfaceDecl::prop_iterator 72 propI = iface->prop_begin(), 73 propE = iface->prop_end(); propI != propE; ++propI) { 74 if (propI->getAtLoc().isInvalid()) 75 continue; 76 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; 77 props.push_back(*propI); 78 } 79 80 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> 81 prop_impl_iterator; 82 for (prop_impl_iterator 83 I = prop_impl_iterator(D->decls_begin()), 84 E = prop_impl_iterator(D->decls_end()); I != E; ++I) { 85 ObjCPropertyImplDecl *implD = *I; 86 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 87 continue; 88 ObjCPropertyDecl *propD = implD->getPropertyDecl(); 89 if (!propD || propD->isInvalidDecl()) 90 continue; 91 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); 92 if (!ivarD || ivarD->isInvalidDecl()) 93 continue; 94 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); 95 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); 96 if (findAtLoc == AtProps.end()) 97 continue; 98 99 PropsTy &props = findAtLoc->second; 100 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 101 if (I->PropD == propD) { 102 I->IvarD = ivarD; 103 I->ImplD = implD; 104 break; 105 } 106 } 107 } 108 109 for (AtPropDeclsTy::iterator 110 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 111 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); 112 PropsTy &props = I->second; 113 QualType ty = getPropertyType(props); 114 if (!ty->isObjCRetainableType()) 115 continue; 116 if (hasIvarWithExplicitOwnership(props)) 117 continue; 118 119 Transaction Trans(Pass.TA); 120 rewriteProperty(props, atLoc); 121 } 122 } 123 124 private: 125 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const { 126 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 127 128 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | 129 ObjCPropertyDecl::OBJC_PR_unsafe_unretained | 130 ObjCPropertyDecl::OBJC_PR_strong | 131 ObjCPropertyDecl::OBJC_PR_weak)) 132 return; 133 134 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { 135 rewriteAttribute("retain", "strong", atLoc); 136 return; 137 } 138 139 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 140 if (hasIvarAssignedAPlusOneObject(props)) { 141 rewriteAttribute("assign", "strong", atLoc); 142 return; 143 } 144 return rewriteAssign(props, atLoc); 145 } 146 147 if (hasIvarAssignedAPlusOneObject(props)) 148 return maybeAddStrongAttr(props, atLoc); 149 150 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 151 } 152 153 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 154 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 155 156 bool rewroteAttr = rewriteAttribute("assign", 157 canUseWeak ? "weak" : "unsafe_unretained", 158 atLoc); 159 if (!rewroteAttr) 160 canUseWeak = false; 161 162 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 163 if (isUserDeclared(I->IvarD)) 164 Pass.TA.insert(I->IvarD->getLocation(), 165 canUseWeak ? "__weak " : "__unsafe_unretained "); 166 if (I->ImplD) 167 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 168 I->ImplD->getLocation()); 169 } 170 } 171 172 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 173 SourceLocation atLoc) const { 174 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 175 176 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 177 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 178 !hasAllIvarsBacked(props)) { 179 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 180 atLoc); 181 if (!addedAttr) 182 canUseWeak = false; 183 } 184 185 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 186 if (isUserDeclared(I->IvarD)) 187 Pass.TA.insert(I->IvarD->getLocation(), 188 canUseWeak ? "__weak " : "__unsafe_unretained "); 189 if (I->ImplD) { 190 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 191 I->ImplD->getLocation()); 192 Pass.TA.clearDiagnostic( 193 diag::err_arc_objc_property_default_assign_on_object, 194 I->ImplD->getLocation()); 195 } 196 } 197 } 198 199 void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const { 200 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 201 202 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 203 !hasAllIvarsBacked(props)) { 204 addAttribute("strong", atLoc); 205 } 206 207 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 208 if (I->ImplD) { 209 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 210 I->ImplD->getLocation()); 211 Pass.TA.clearDiagnostic( 212 diag::err_arc_objc_property_default_assign_on_object, 213 I->ImplD->getLocation()); 214 } 215 } 216 } 217 218 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 219 SourceLocation atLoc) const { 220 if (atLoc.isMacroID()) 221 return false; 222 223 SourceManager &SM = Pass.Ctx.getSourceManager(); 224 225 // Break down the source location. 226 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 227 228 // Try to load the file buffer. 229 bool invalidTemp = false; 230 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 231 if (invalidTemp) 232 return false; 233 234 const char *tokenBegin = file.data() + locInfo.second; 235 236 // Lex from the start of the given location. 237 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 238 Pass.Ctx.getLangOptions(), 239 file.begin(), tokenBegin, file.end()); 240 Token tok; 241 lexer.LexFromRawLexer(tok); 242 if (tok.isNot(tok::at)) return false; 243 lexer.LexFromRawLexer(tok); 244 if (tok.isNot(tok::raw_identifier)) return false; 245 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 246 != "property") 247 return false; 248 lexer.LexFromRawLexer(tok); 249 if (tok.isNot(tok::l_paren)) return false; 250 251 lexer.LexFromRawLexer(tok); 252 if (tok.is(tok::r_paren)) 253 return false; 254 255 while (1) { 256 if (tok.isNot(tok::raw_identifier)) return false; 257 StringRef ident(tok.getRawIdentifierData(), tok.getLength()); 258 if (ident == fromAttr) { 259 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); 260 return true; 261 } 262 263 do { 264 lexer.LexFromRawLexer(tok); 265 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); 266 if (tok.is(tok::r_paren)) 267 break; 268 lexer.LexFromRawLexer(tok); 269 } 270 271 return false; 272 } 273 274 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 275 if (atLoc.isMacroID()) 276 return false; 277 278 SourceManager &SM = Pass.Ctx.getSourceManager(); 279 280 // Break down the source location. 281 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 282 283 // Try to load the file buffer. 284 bool invalidTemp = false; 285 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 286 if (invalidTemp) 287 return false; 288 289 const char *tokenBegin = file.data() + locInfo.second; 290 291 // Lex from the start of the given location. 292 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 293 Pass.Ctx.getLangOptions(), 294 file.begin(), tokenBegin, file.end()); 295 Token tok; 296 lexer.LexFromRawLexer(tok); 297 if (tok.isNot(tok::at)) return false; 298 lexer.LexFromRawLexer(tok); 299 if (tok.isNot(tok::raw_identifier)) return false; 300 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 301 != "property") 302 return false; 303 lexer.LexFromRawLexer(tok); 304 305 if (tok.isNot(tok::l_paren)) { 306 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); 307 return true; 308 } 309 310 lexer.LexFromRawLexer(tok); 311 if (tok.is(tok::r_paren)) { 312 Pass.TA.insert(tok.getLocation(), attr); 313 return true; 314 } 315 316 if (tok.isNot(tok::raw_identifier)) return false; 317 318 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); 319 return true; 320 } 321 322 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 323 ObjCIvarDecl *Ivar; 324 public: 325 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 326 327 bool VisitBinAssign(BinaryOperator *E) { 328 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 329 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 330 if (RE->getDecl() != Ivar) 331 return true; 332 333 if (ObjCMessageExpr * 334 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) 335 if (ME->getMethodFamily() == OMF_retain) 336 return false; 337 338 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); 339 while (implCE && implCE->getCastKind() == CK_BitCast) 340 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); 341 342 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) 343 return false; 344 } 345 346 return true; 347 } 348 }; 349 350 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 351 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 352 PlusOneAssign oneAssign(I->IvarD); 353 bool notFound = oneAssign.TraverseDecl(CurImplD); 354 if (!notFound) 355 return true; 356 } 357 358 return false; 359 } 360 361 bool hasIvarWithExplicitOwnership(PropsTy &props) const { 362 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 363 if (isUserDeclared(I->IvarD)) { 364 if (isa<AttributedType>(I->IvarD->getType())) 365 return true; 366 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 367 != Qualifiers::OCL_Strong) 368 return true; 369 } 370 } 371 372 return false; 373 } 374 375 bool hasAllIvarsBacked(PropsTy &props) const { 376 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 377 if (!isUserDeclared(I->IvarD)) 378 return false; 379 380 return true; 381 } 382 383 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 384 return ivarD && !ivarD->getSynthesize(); 385 } 386 387 QualType getPropertyType(PropsTy &props) const { 388 assert(!props.empty()); 389 QualType ty = props[0].PropD->getType(); 390 391 #ifndef NDEBUG 392 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 393 assert(ty == I->PropD->getType()); 394 #endif 395 396 return ty; 397 } 398 399 ObjCPropertyDecl::PropertyAttributeKind 400 getPropertyAttrs(PropsTy &props) const { 401 assert(!props.empty()); 402 ObjCPropertyDecl::PropertyAttributeKind 403 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 404 405 #ifndef NDEBUG 406 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 407 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 408 #endif 409 410 return attrs; 411 } 412 }; 413 414 class ImplementationChecker : 415 public RecursiveASTVisitor<ImplementationChecker> { 416 MigrationPass &Pass; 417 418 public: 419 ImplementationChecker(MigrationPass &pass) : Pass(pass) { } 420 421 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { 422 PropertiesRewriter(Pass).doTransform(D); 423 return true; 424 } 425 }; 426 427 } // anonymous namespace 428 429 void trans::rewriteProperties(MigrationPass &pass) { 430 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 431 } 432