1 //===--- TransGCAttrs.cpp - Transformations 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 #include "Transforms.h" 11 #include "Internals.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/Basic/SourceManager.h" 14 #include "clang/Lex/Lexer.h" 15 #include "clang/Sema/SemaDiagnostic.h" 16 #include "llvm/ADT/SmallString.h" 17 #include "llvm/ADT/TinyPtrVector.h" 18 #include "llvm/Support/SaveAndRestore.h" 19 20 using namespace clang; 21 using namespace arcmt; 22 using namespace trans; 23 24 namespace { 25 26 /// Collects all the places where GC attributes __strong/__weak occur. 27 class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> { 28 MigrationContext &MigrateCtx; 29 bool FullyMigratable; 30 std::vector<ObjCPropertyDecl *> &AllProps; 31 32 typedef RecursiveASTVisitor<GCAttrsCollector> base; 33 public: 34 GCAttrsCollector(MigrationContext &ctx, 35 std::vector<ObjCPropertyDecl *> &AllProps) 36 : MigrateCtx(ctx), FullyMigratable(false), 37 AllProps(AllProps) { } 38 39 bool shouldWalkTypesOfTypeLocs() const { return false; } 40 41 bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { 42 handleAttr(TL); 43 return true; 44 } 45 46 bool TraverseDecl(Decl *D) { 47 if (!D || D->isImplicit()) 48 return true; 49 50 SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D)); 51 52 if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) { 53 lookForAttribute(PropD, PropD->getTypeSourceInfo()); 54 AllProps.push_back(PropD); 55 } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { 56 lookForAttribute(DD, DD->getTypeSourceInfo()); 57 } 58 return base::TraverseDecl(D); 59 } 60 61 void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { 62 if (!TInfo) 63 return; 64 TypeLoc TL = TInfo->getTypeLoc(); 65 while (TL) { 66 if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) { 67 TL = QL.getUnqualifiedLoc(); 68 } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) { 69 if (handleAttr(Attr, D)) 70 break; 71 TL = Attr.getModifiedLoc(); 72 } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) { 73 TL = Arr.getElementLoc(); 74 } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) { 75 TL = PT.getPointeeLoc(); 76 } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>()) 77 TL = RT.getPointeeLoc(); 78 else 79 break; 80 } 81 } 82 83 bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) { 84 auto *OwnershipAttr = TL.getAttrAs<ObjCOwnershipAttr>(); 85 if (!OwnershipAttr) 86 return false; 87 88 SourceLocation Loc = OwnershipAttr->getLocation(); 89 unsigned RawLoc = Loc.getRawEncoding(); 90 if (MigrateCtx.AttrSet.count(RawLoc)) 91 return true; 92 93 ASTContext &Ctx = MigrateCtx.Pass.Ctx; 94 SourceManager &SM = Ctx.getSourceManager(); 95 if (Loc.isMacroID()) 96 Loc = SM.getImmediateExpansionRange(Loc).getBegin(); 97 StringRef Spell = OwnershipAttr->getKind()->getName(); 98 MigrationContext::GCAttrOccurrence::AttrKind Kind; 99 if (Spell == "strong") 100 Kind = MigrationContext::GCAttrOccurrence::Strong; 101 else if (Spell == "weak") 102 Kind = MigrationContext::GCAttrOccurrence::Weak; 103 else 104 return false; 105 106 MigrateCtx.AttrSet.insert(RawLoc); 107 MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); 108 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); 109 110 Attr.Kind = Kind; 111 Attr.Loc = Loc; 112 Attr.ModifiedType = TL.getModifiedLoc().getType(); 113 Attr.Dcl = D; 114 Attr.FullyMigratable = FullyMigratable; 115 return true; 116 } 117 118 bool isMigratable(Decl *D) { 119 if (isa<TranslationUnitDecl>(D)) 120 return false; 121 122 if (isInMainFile(D)) 123 return true; 124 125 if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) 126 return FD->hasBody(); 127 128 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) 129 return hasObjCImpl(ContD); 130 131 if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { 132 for (const auto *MI : RD->methods()) { 133 if (MI->isOutOfLine()) 134 return true; 135 } 136 return false; 137 } 138 139 return isMigratable(cast<Decl>(D->getDeclContext())); 140 } 141 142 static bool hasObjCImpl(Decl *D) { 143 if (!D) 144 return false; 145 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) { 146 if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD)) 147 return ID->getImplementation() != nullptr; 148 if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD)) 149 return CD->getImplementation() != nullptr; 150 return isa<ObjCImplDecl>(ContD); 151 } 152 return false; 153 } 154 155 bool isInMainFile(Decl *D) { 156 if (!D) 157 return false; 158 159 for (auto I : D->redecls()) 160 if (!isInMainFile(I->getLocation())) 161 return false; 162 163 return true; 164 } 165 166 bool isInMainFile(SourceLocation Loc) { 167 if (Loc.isInvalid()) 168 return false; 169 170 SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); 171 return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); 172 } 173 }; 174 175 } // anonymous namespace 176 177 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) { 178 TransformActions &TA = MigrateCtx.Pass.TA; 179 180 for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 181 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 182 if (Attr.FullyMigratable && Attr.Dcl) { 183 if (Attr.ModifiedType.isNull()) 184 continue; 185 if (!Attr.ModifiedType->isObjCRetainableType()) { 186 TA.reportError("GC managed memory will become unmanaged in ARC", 187 Attr.Loc); 188 } 189 } 190 } 191 } 192 193 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) { 194 TransformActions &TA = MigrateCtx.Pass.TA; 195 196 for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 197 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 198 if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) { 199 if (Attr.ModifiedType.isNull() || 200 !Attr.ModifiedType->isObjCRetainableType()) 201 continue; 202 if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType, 203 /*AllowOnUnknownClass=*/true)) { 204 Transaction Trans(TA); 205 if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding())) 206 TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained"); 207 TA.clearDiagnostic(diag::err_arc_weak_no_runtime, 208 diag::err_arc_unsupported_weak_class, 209 Attr.Loc); 210 } 211 } 212 } 213 } 214 215 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 216 217 static void checkAllAtProps(MigrationContext &MigrateCtx, 218 SourceLocation AtLoc, 219 IndivPropsTy &IndProps) { 220 if (IndProps.empty()) 221 return; 222 223 for (IndivPropsTy::iterator 224 PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 225 QualType T = (*PI)->getType(); 226 if (T.isNull() || !T->isObjCRetainableType()) 227 return; 228 } 229 230 SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs; 231 bool hasWeak = false, hasStrong = false; 232 ObjCPropertyDecl::PropertyAttributeKind 233 Attrs = ObjCPropertyDecl::OBJC_PR_noattr; 234 for (IndivPropsTy::iterator 235 PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 236 ObjCPropertyDecl *PD = *PI; 237 Attrs = PD->getPropertyAttributesAsWritten(); 238 TypeSourceInfo *TInfo = PD->getTypeSourceInfo(); 239 if (!TInfo) 240 return; 241 TypeLoc TL = TInfo->getTypeLoc(); 242 if (AttributedTypeLoc ATL = 243 TL.getAs<AttributedTypeLoc>()) { 244 ATLs.push_back(std::make_pair(ATL, PD)); 245 if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { 246 hasWeak = true; 247 } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) 248 hasStrong = true; 249 else 250 return; 251 } 252 } 253 if (ATLs.empty()) 254 return; 255 if (hasWeak && hasStrong) 256 return; 257 258 TransformActions &TA = MigrateCtx.Pass.TA; 259 Transaction Trans(TA); 260 261 if (GCAttrsCollector::hasObjCImpl( 262 cast<Decl>(IndProps.front()->getDeclContext()))) { 263 if (hasWeak) 264 MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding()); 265 266 } else { 267 StringRef toAttr = "strong"; 268 if (hasWeak) { 269 if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(), 270 /*AllowOnUnkwownClass=*/true)) 271 toAttr = "weak"; 272 else 273 toAttr = "unsafe_unretained"; 274 } 275 if (Attrs & ObjCPropertyDecl::OBJC_PR_assign) 276 MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc); 277 else 278 MigrateCtx.addPropertyAttribute(toAttr, AtLoc); 279 } 280 281 for (unsigned i = 0, e = ATLs.size(); i != e; ++i) { 282 SourceLocation Loc = ATLs[i].first.getAttr()->getLocation(); 283 if (Loc.isMacroID()) 284 Loc = MigrateCtx.Pass.Ctx.getSourceManager() 285 .getImmediateExpansionRange(Loc) 286 .getBegin(); 287 TA.remove(Loc); 288 TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc); 289 TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership, 290 ATLs[i].second->getLocation()); 291 MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding()); 292 } 293 } 294 295 static void checkAllProps(MigrationContext &MigrateCtx, 296 std::vector<ObjCPropertyDecl *> &AllProps) { 297 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 298 llvm::DenseMap<unsigned, IndivPropsTy> AtProps; 299 300 for (unsigned i = 0, e = AllProps.size(); i != e; ++i) { 301 ObjCPropertyDecl *PD = AllProps[i]; 302 if (PD->getPropertyAttributesAsWritten() & 303 (ObjCPropertyDecl::OBJC_PR_assign | 304 ObjCPropertyDecl::OBJC_PR_readonly)) { 305 SourceLocation AtLoc = PD->getAtLoc(); 306 if (AtLoc.isInvalid()) 307 continue; 308 unsigned RawAt = AtLoc.getRawEncoding(); 309 AtProps[RawAt].push_back(PD); 310 } 311 } 312 313 for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator 314 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 315 SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first); 316 IndivPropsTy &IndProps = I->second; 317 checkAllAtProps(MigrateCtx, AtLoc, IndProps); 318 } 319 } 320 321 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { 322 std::vector<ObjCPropertyDecl *> AllProps; 323 GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl( 324 MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); 325 326 errorForGCAttrsOnNonObjC(MigrateCtx); 327 checkAllProps(MigrateCtx, AllProps); 328 checkWeakGCAttrs(MigrateCtx); 329 } 330 331 void MigrationContext::dumpGCAttrs() { 332 llvm::errs() << "\n################\n"; 333 for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) { 334 GCAttrOccurrence &Attr = GCAttrs[i]; 335 llvm::errs() << "KIND: " 336 << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak"); 337 llvm::errs() << "\nLOC: "; 338 Attr.Loc.print(llvm::errs(), Pass.Ctx.getSourceManager()); 339 llvm::errs() << "\nTYPE: "; 340 Attr.ModifiedType.dump(); 341 if (Attr.Dcl) { 342 llvm::errs() << "DECL:\n"; 343 Attr.Dcl->dump(); 344 } else { 345 llvm::errs() << "DECL: NONE"; 346 } 347 llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; 348 llvm::errs() << "\n----------------\n"; 349 } 350 llvm::errs() << "\n################\n"; 351 } 352