TransProperties.cpp revision 01b2b9bb154d69cc695717876e903329f6f0973c
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 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 62public: 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 124private: 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 if (propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) 136 rewriteAttribute("retain", "strong", atLoc); 137 else 138 removeAttribute("retain", atLoc); // strong is the default. 139 return; 140 } 141 142 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { 143 if (hasIvarAssignedAPlusOneObject(props)) { 144 rewriteAttribute("assign", "strong", atLoc); 145 return; 146 } 147 return rewriteAssign(props, atLoc); 148 } 149 150 if (hasIvarAssignedAPlusOneObject(props)) 151 return maybeAddStrongAttr(props, atLoc); 152 153 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); 154 } 155 156 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { 157 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 158 159 bool rewroteAttr = rewriteAttribute("assign", 160 canUseWeak ? "weak" : "unsafe_unretained", 161 atLoc); 162 if (!rewroteAttr) 163 canUseWeak = false; 164 165 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 166 if (isUserDeclared(I->IvarD)) 167 Pass.TA.insert(I->IvarD->getLocation(), 168 canUseWeak ? "__weak " : "__unsafe_unretained "); 169 if (I->ImplD) 170 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 171 I->ImplD->getLocation()); 172 } 173 } 174 175 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, 176 SourceLocation atLoc) const { 177 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 178 179 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); 180 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 181 !hasAllIvarsBacked(props)) { 182 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", 183 atLoc); 184 if (!addedAttr) 185 canUseWeak = false; 186 } 187 188 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 189 if (isUserDeclared(I->IvarD)) 190 Pass.TA.insert(I->IvarD->getLocation(), 191 canUseWeak ? "__weak " : "__unsafe_unretained "); 192 if (I->ImplD) { 193 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 194 I->ImplD->getLocation()); 195 Pass.TA.clearDiagnostic( 196 diag::err_arc_objc_property_default_assign_on_object, 197 I->ImplD->getLocation()); 198 } 199 } 200 } 201 202 void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const { 203 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); 204 205 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || 206 !hasAllIvarsBacked(props)) { 207 addAttribute("strong", atLoc); 208 } 209 210 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 211 if (I->ImplD) { 212 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, 213 I->ImplD->getLocation()); 214 Pass.TA.clearDiagnostic( 215 diag::err_arc_objc_property_default_assign_on_object, 216 I->ImplD->getLocation()); 217 } 218 } 219 } 220 221 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const { 222 return rewriteAttribute(fromAttr, StringRef(), atLoc); 223 } 224 225 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, 226 SourceLocation atLoc) const { 227 if (atLoc.isMacroID()) 228 return false; 229 230 SourceManager &SM = Pass.Ctx.getSourceManager(); 231 232 // Break down the source location. 233 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 234 235 // Try to load the file buffer. 236 bool invalidTemp = false; 237 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 238 if (invalidTemp) 239 return false; 240 241 const char *tokenBegin = file.data() + locInfo.second; 242 243 // Lex from the start of the given location. 244 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 245 Pass.Ctx.getLangOptions(), 246 file.begin(), tokenBegin, file.end()); 247 Token tok; 248 lexer.LexFromRawLexer(tok); 249 if (tok.isNot(tok::at)) return false; 250 lexer.LexFromRawLexer(tok); 251 if (tok.isNot(tok::raw_identifier)) return false; 252 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 253 != "property") 254 return false; 255 lexer.LexFromRawLexer(tok); 256 if (tok.isNot(tok::l_paren)) return false; 257 258 Token BeforeTok = tok; 259 Token AfterTok; 260 AfterTok.startToken(); 261 SourceLocation AttrLoc; 262 263 lexer.LexFromRawLexer(tok); 264 if (tok.is(tok::r_paren)) 265 return false; 266 267 while (1) { 268 if (tok.isNot(tok::raw_identifier)) return false; 269 StringRef ident(tok.getRawIdentifierData(), tok.getLength()); 270 if (ident == fromAttr) { 271 if (!toAttr.empty()) { 272 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); 273 return true; 274 } 275 // We want to remove the attribute. 276 AttrLoc = tok.getLocation(); 277 } 278 279 do { 280 lexer.LexFromRawLexer(tok); 281 if (AttrLoc.isValid() && AfterTok.is(tok::unknown)) 282 AfterTok = tok; 283 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); 284 if (tok.is(tok::r_paren)) 285 break; 286 if (AttrLoc.isInvalid()) 287 BeforeTok = tok; 288 lexer.LexFromRawLexer(tok); 289 } 290 291 if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) { 292 // We want to remove the attribute. 293 if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) { 294 Pass.TA.remove(SourceRange(BeforeTok.getLocation(), 295 AfterTok.getLocation())); 296 } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) { 297 Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation())); 298 } else { 299 Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc)); 300 } 301 302 return true; 303 } 304 305 return false; 306 } 307 308 bool addAttribute(StringRef attr, SourceLocation atLoc) const { 309 if (atLoc.isMacroID()) 310 return false; 311 312 SourceManager &SM = Pass.Ctx.getSourceManager(); 313 314 // Break down the source location. 315 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); 316 317 // Try to load the file buffer. 318 bool invalidTemp = false; 319 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 320 if (invalidTemp) 321 return false; 322 323 const char *tokenBegin = file.data() + locInfo.second; 324 325 // Lex from the start of the given location. 326 Lexer lexer(SM.getLocForStartOfFile(locInfo.first), 327 Pass.Ctx.getLangOptions(), 328 file.begin(), tokenBegin, file.end()); 329 Token tok; 330 lexer.LexFromRawLexer(tok); 331 if (tok.isNot(tok::at)) return false; 332 lexer.LexFromRawLexer(tok); 333 if (tok.isNot(tok::raw_identifier)) return false; 334 if (StringRef(tok.getRawIdentifierData(), tok.getLength()) 335 != "property") 336 return false; 337 lexer.LexFromRawLexer(tok); 338 339 if (tok.isNot(tok::l_paren)) { 340 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); 341 return true; 342 } 343 344 lexer.LexFromRawLexer(tok); 345 if (tok.is(tok::r_paren)) { 346 Pass.TA.insert(tok.getLocation(), attr); 347 return true; 348 } 349 350 if (tok.isNot(tok::raw_identifier)) return false; 351 352 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); 353 return true; 354 } 355 356 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> { 357 ObjCIvarDecl *Ivar; 358 public: 359 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} 360 361 bool VisitBinAssign(BinaryOperator *E) { 362 Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); 363 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) { 364 if (RE->getDecl() != Ivar) 365 return true; 366 367 if (ObjCMessageExpr * 368 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) 369 if (ME->getMethodFamily() == OMF_retain) 370 return false; 371 372 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); 373 while (implCE && implCE->getCastKind() == CK_BitCast) 374 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); 375 376 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) 377 return false; 378 } 379 380 return true; 381 } 382 }; 383 384 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { 385 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 386 PlusOneAssign oneAssign(I->IvarD); 387 bool notFound = oneAssign.TraverseDecl(CurImplD); 388 if (!notFound) 389 return true; 390 } 391 392 return false; 393 } 394 395 bool hasIvarWithExplicitOwnership(PropsTy &props) const { 396 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { 397 if (isUserDeclared(I->IvarD)) { 398 if (isa<AttributedType>(I->IvarD->getType())) 399 return true; 400 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() 401 != Qualifiers::OCL_Strong) 402 return true; 403 } 404 } 405 406 return false; 407 } 408 409 bool hasAllIvarsBacked(PropsTy &props) const { 410 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 411 if (!isUserDeclared(I->IvarD)) 412 return false; 413 414 return true; 415 } 416 417 bool isUserDeclared(ObjCIvarDecl *ivarD) const { 418 return ivarD && !ivarD->getSynthesize(); 419 } 420 421 QualType getPropertyType(PropsTy &props) const { 422 assert(!props.empty()); 423 QualType ty = props[0].PropD->getType(); 424 425#ifndef NDEBUG 426 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 427 assert(ty == I->PropD->getType()); 428#endif 429 430 return ty; 431 } 432 433 ObjCPropertyDecl::PropertyAttributeKind 434 getPropertyAttrs(PropsTy &props) const { 435 assert(!props.empty()); 436 ObjCPropertyDecl::PropertyAttributeKind 437 attrs = props[0].PropD->getPropertyAttributesAsWritten(); 438 439#ifndef NDEBUG 440 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) 441 assert(attrs == I->PropD->getPropertyAttributesAsWritten()); 442#endif 443 444 return attrs; 445 } 446}; 447 448class ImplementationChecker : 449 public RecursiveASTVisitor<ImplementationChecker> { 450 MigrationPass &Pass; 451 452public: 453 ImplementationChecker(MigrationPass &pass) : Pass(pass) { } 454 455 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { 456 PropertiesRewriter(Pass).doTransform(D); 457 return true; 458 } 459}; 460 461} // anonymous namespace 462 463void trans::rewriteProperties(MigrationPass &pass) { 464 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 465} 466