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