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