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