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