1234287Sdim//===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim 10234287Sdim#include "Transforms.h" 11234287Sdim#include "Internals.h" 12239462Sdim#include "clang/AST/ASTContext.h" 13239462Sdim#include "clang/Basic/SourceManager.h" 14234287Sdim#include "clang/Lex/Lexer.h" 15234287Sdim#include "clang/Sema/SemaDiagnostic.h" 16234287Sdim#include "llvm/ADT/SmallString.h" 17234287Sdim#include "llvm/ADT/TinyPtrVector.h" 18239462Sdim#include "llvm/Support/SaveAndRestore.h" 19234287Sdim 20234287Sdimusing namespace clang; 21234287Sdimusing namespace arcmt; 22234287Sdimusing namespace trans; 23234287Sdim 24234287Sdimnamespace { 25234287Sdim 26234287Sdim/// \brief Collects all the places where GC attributes __strong/__weak occur. 27234287Sdimclass GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> { 28234287Sdim MigrationContext &MigrateCtx; 29234287Sdim bool FullyMigratable; 30234287Sdim std::vector<ObjCPropertyDecl *> &AllProps; 31234287Sdim 32234287Sdim typedef RecursiveASTVisitor<GCAttrsCollector> base; 33234287Sdimpublic: 34234287Sdim GCAttrsCollector(MigrationContext &ctx, 35234287Sdim std::vector<ObjCPropertyDecl *> &AllProps) 36234287Sdim : MigrateCtx(ctx), FullyMigratable(false), 37234287Sdim AllProps(AllProps) { } 38234287Sdim 39234287Sdim bool shouldWalkTypesOfTypeLocs() const { return false; } 40234287Sdim 41234287Sdim bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { 42234287Sdim handleAttr(TL); 43234287Sdim return true; 44234287Sdim } 45234287Sdim 46234287Sdim bool TraverseDecl(Decl *D) { 47234287Sdim if (!D || D->isImplicit()) 48234287Sdim return true; 49234287Sdim 50234287Sdim SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D)); 51234287Sdim 52234287Sdim if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) { 53234287Sdim lookForAttribute(PropD, PropD->getTypeSourceInfo()); 54234287Sdim AllProps.push_back(PropD); 55234287Sdim } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { 56234287Sdim lookForAttribute(DD, DD->getTypeSourceInfo()); 57234287Sdim } 58234287Sdim return base::TraverseDecl(D); 59234287Sdim } 60234287Sdim 61234287Sdim void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { 62234287Sdim if (!TInfo) 63234287Sdim return; 64234287Sdim TypeLoc TL = TInfo->getTypeLoc(); 65234287Sdim while (TL) { 66249423Sdim if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) { 67249423Sdim TL = QL.getUnqualifiedLoc(); 68249423Sdim } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) { 69249423Sdim if (handleAttr(Attr, D)) 70234287Sdim break; 71249423Sdim TL = Attr.getModifiedLoc(); 72249423Sdim } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) { 73249423Sdim TL = Arr.getElementLoc(); 74249423Sdim } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) { 75249423Sdim TL = PT.getPointeeLoc(); 76249423Sdim } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>()) 77249423Sdim TL = RT.getPointeeLoc(); 78234287Sdim else 79234287Sdim break; 80234287Sdim } 81234287Sdim } 82234287Sdim 83276479Sdim bool handleAttr(AttributedTypeLoc TL, Decl *D = nullptr) { 84234287Sdim if (TL.getAttrKind() != AttributedType::attr_objc_ownership) 85234287Sdim return false; 86234287Sdim 87234287Sdim SourceLocation Loc = TL.getAttrNameLoc(); 88234287Sdim unsigned RawLoc = Loc.getRawEncoding(); 89234287Sdim if (MigrateCtx.AttrSet.count(RawLoc)) 90234287Sdim return true; 91234287Sdim 92234287Sdim ASTContext &Ctx = MigrateCtx.Pass.Ctx; 93234287Sdim SourceManager &SM = Ctx.getSourceManager(); 94234287Sdim if (Loc.isMacroID()) 95234287Sdim Loc = SM.getImmediateExpansionRange(Loc).first; 96234287Sdim SmallString<32> Buf; 97234287Sdim bool Invalid = false; 98234287Sdim StringRef Spell = Lexer::getSpelling( 99234287Sdim SM.getSpellingLoc(TL.getAttrEnumOperandLoc()), 100234287Sdim Buf, SM, Ctx.getLangOpts(), &Invalid); 101234287Sdim if (Invalid) 102234287Sdim return false; 103234287Sdim MigrationContext::GCAttrOccurrence::AttrKind Kind; 104234287Sdim if (Spell == "strong") 105234287Sdim Kind = MigrationContext::GCAttrOccurrence::Strong; 106234287Sdim else if (Spell == "weak") 107234287Sdim Kind = MigrationContext::GCAttrOccurrence::Weak; 108234287Sdim else 109234287Sdim return false; 110234287Sdim 111234287Sdim MigrateCtx.AttrSet.insert(RawLoc); 112234287Sdim MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); 113234287Sdim MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); 114234287Sdim 115234287Sdim Attr.Kind = Kind; 116234287Sdim Attr.Loc = Loc; 117234287Sdim Attr.ModifiedType = TL.getModifiedLoc().getType(); 118234287Sdim Attr.Dcl = D; 119234287Sdim Attr.FullyMigratable = FullyMigratable; 120234287Sdim return true; 121234287Sdim } 122234287Sdim 123234287Sdim bool isMigratable(Decl *D) { 124234287Sdim if (isa<TranslationUnitDecl>(D)) 125234287Sdim return false; 126234287Sdim 127234287Sdim if (isInMainFile(D)) 128234287Sdim return true; 129234287Sdim 130234287Sdim if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) 131234287Sdim return FD->hasBody(); 132234287Sdim 133234287Sdim if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) 134234287Sdim return hasObjCImpl(ContD); 135234287Sdim 136234287Sdim if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { 137276479Sdim for (const auto *MI : RD->methods()) { 138239462Sdim if (MI->isOutOfLine()) 139234287Sdim return true; 140234287Sdim } 141234287Sdim return false; 142234287Sdim } 143234287Sdim 144234287Sdim return isMigratable(cast<Decl>(D->getDeclContext())); 145234287Sdim } 146234287Sdim 147234287Sdim static bool hasObjCImpl(Decl *D) { 148234287Sdim if (!D) 149234287Sdim return false; 150234287Sdim if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) { 151234287Sdim if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD)) 152276479Sdim return ID->getImplementation() != nullptr; 153234287Sdim if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD)) 154276479Sdim return CD->getImplementation() != nullptr; 155296417Sdim return isa<ObjCImplDecl>(ContD); 156234287Sdim } 157234287Sdim return false; 158234287Sdim } 159234287Sdim 160234287Sdim bool isInMainFile(Decl *D) { 161234287Sdim if (!D) 162234287Sdim return false; 163234287Sdim 164276479Sdim for (auto I : D->redecls()) 165239462Sdim if (!isInMainFile(I->getLocation())) 166234287Sdim return false; 167234287Sdim 168234287Sdim return true; 169234287Sdim } 170234287Sdim 171234287Sdim bool isInMainFile(SourceLocation Loc) { 172234287Sdim if (Loc.isInvalid()) 173234287Sdim return false; 174234287Sdim 175234287Sdim SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); 176234287Sdim return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); 177234287Sdim } 178234287Sdim}; 179234287Sdim 180234287Sdim} // anonymous namespace 181234287Sdim 182234287Sdimstatic void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) { 183234287Sdim TransformActions &TA = MigrateCtx.Pass.TA; 184234287Sdim 185234287Sdim for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 186234287Sdim MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 187234287Sdim if (Attr.FullyMigratable && Attr.Dcl) { 188234287Sdim if (Attr.ModifiedType.isNull()) 189234287Sdim continue; 190234287Sdim if (!Attr.ModifiedType->isObjCRetainableType()) { 191234287Sdim TA.reportError("GC managed memory will become unmanaged in ARC", 192234287Sdim Attr.Loc); 193234287Sdim } 194234287Sdim } 195234287Sdim } 196234287Sdim} 197234287Sdim 198234287Sdimstatic void checkWeakGCAttrs(MigrationContext &MigrateCtx) { 199234287Sdim TransformActions &TA = MigrateCtx.Pass.TA; 200234287Sdim 201234287Sdim for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 202234287Sdim MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 203234287Sdim if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) { 204234287Sdim if (Attr.ModifiedType.isNull() || 205234287Sdim !Attr.ModifiedType->isObjCRetainableType()) 206234287Sdim continue; 207234287Sdim if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType, 208234287Sdim /*AllowOnUnknownClass=*/true)) { 209234287Sdim Transaction Trans(TA); 210234287Sdim if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding())) 211234287Sdim TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained"); 212234287Sdim TA.clearDiagnostic(diag::err_arc_weak_no_runtime, 213234287Sdim diag::err_arc_unsupported_weak_class, 214234287Sdim Attr.Loc); 215234287Sdim } 216234287Sdim } 217234287Sdim } 218234287Sdim} 219234287Sdim 220234287Sdimtypedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 221234287Sdim 222234287Sdimstatic void checkAllAtProps(MigrationContext &MigrateCtx, 223234287Sdim SourceLocation AtLoc, 224234287Sdim IndivPropsTy &IndProps) { 225234287Sdim if (IndProps.empty()) 226234287Sdim return; 227234287Sdim 228234287Sdim for (IndivPropsTy::iterator 229234287Sdim PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 230234287Sdim QualType T = (*PI)->getType(); 231234287Sdim if (T.isNull() || !T->isObjCRetainableType()) 232234287Sdim return; 233234287Sdim } 234234287Sdim 235234287Sdim SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs; 236234287Sdim bool hasWeak = false, hasStrong = false; 237234287Sdim ObjCPropertyDecl::PropertyAttributeKind 238234287Sdim Attrs = ObjCPropertyDecl::OBJC_PR_noattr; 239234287Sdim for (IndivPropsTy::iterator 240234287Sdim PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 241234287Sdim ObjCPropertyDecl *PD = *PI; 242234287Sdim Attrs = PD->getPropertyAttributesAsWritten(); 243234287Sdim TypeSourceInfo *TInfo = PD->getTypeSourceInfo(); 244234287Sdim if (!TInfo) 245234287Sdim return; 246234287Sdim TypeLoc TL = TInfo->getTypeLoc(); 247249423Sdim if (AttributedTypeLoc ATL = 248249423Sdim TL.getAs<AttributedTypeLoc>()) { 249249423Sdim ATLs.push_back(std::make_pair(ATL, PD)); 250234287Sdim if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { 251234287Sdim hasWeak = true; 252234287Sdim } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) 253234287Sdim hasStrong = true; 254234287Sdim else 255234287Sdim return; 256234287Sdim } 257234287Sdim } 258234287Sdim if (ATLs.empty()) 259234287Sdim return; 260234287Sdim if (hasWeak && hasStrong) 261234287Sdim return; 262234287Sdim 263234287Sdim TransformActions &TA = MigrateCtx.Pass.TA; 264234287Sdim Transaction Trans(TA); 265234287Sdim 266234287Sdim if (GCAttrsCollector::hasObjCImpl( 267234287Sdim cast<Decl>(IndProps.front()->getDeclContext()))) { 268234287Sdim if (hasWeak) 269234287Sdim MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding()); 270234287Sdim 271234287Sdim } else { 272234287Sdim StringRef toAttr = "strong"; 273234287Sdim if (hasWeak) { 274234287Sdim if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(), 275234287Sdim /*AllowOnUnkwownClass=*/true)) 276234287Sdim toAttr = "weak"; 277234287Sdim else 278234287Sdim toAttr = "unsafe_unretained"; 279234287Sdim } 280234287Sdim if (Attrs & ObjCPropertyDecl::OBJC_PR_assign) 281234287Sdim MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc); 282234287Sdim else 283234287Sdim MigrateCtx.addPropertyAttribute(toAttr, AtLoc); 284234287Sdim } 285234287Sdim 286234287Sdim for (unsigned i = 0, e = ATLs.size(); i != e; ++i) { 287234287Sdim SourceLocation Loc = ATLs[i].first.getAttrNameLoc(); 288234287Sdim if (Loc.isMacroID()) 289234287Sdim Loc = MigrateCtx.Pass.Ctx.getSourceManager() 290234287Sdim .getImmediateExpansionRange(Loc).first; 291234287Sdim TA.remove(Loc); 292234287Sdim TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc); 293234287Sdim TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership, 294234287Sdim ATLs[i].second->getLocation()); 295234287Sdim MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding()); 296234287Sdim } 297234287Sdim} 298234287Sdim 299234287Sdimstatic void checkAllProps(MigrationContext &MigrateCtx, 300234287Sdim std::vector<ObjCPropertyDecl *> &AllProps) { 301234287Sdim typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 302234287Sdim llvm::DenseMap<unsigned, IndivPropsTy> AtProps; 303234287Sdim 304234287Sdim for (unsigned i = 0, e = AllProps.size(); i != e; ++i) { 305234287Sdim ObjCPropertyDecl *PD = AllProps[i]; 306234287Sdim if (PD->getPropertyAttributesAsWritten() & 307234287Sdim (ObjCPropertyDecl::OBJC_PR_assign | 308234287Sdim ObjCPropertyDecl::OBJC_PR_readonly)) { 309234287Sdim SourceLocation AtLoc = PD->getAtLoc(); 310234287Sdim if (AtLoc.isInvalid()) 311234287Sdim continue; 312234287Sdim unsigned RawAt = AtLoc.getRawEncoding(); 313234287Sdim AtProps[RawAt].push_back(PD); 314234287Sdim } 315234287Sdim } 316234287Sdim 317234287Sdim for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator 318234287Sdim I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 319234287Sdim SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first); 320234287Sdim IndivPropsTy &IndProps = I->second; 321234287Sdim checkAllAtProps(MigrateCtx, AtLoc, IndProps); 322234287Sdim } 323234287Sdim} 324234287Sdim 325234287Sdimvoid GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { 326234287Sdim std::vector<ObjCPropertyDecl *> AllProps; 327234287Sdim GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl( 328234287Sdim MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); 329234287Sdim 330234287Sdim errorForGCAttrsOnNonObjC(MigrateCtx); 331234287Sdim checkAllProps(MigrateCtx, AllProps); 332234287Sdim checkWeakGCAttrs(MigrateCtx); 333234287Sdim} 334234287Sdim 335234287Sdimvoid MigrationContext::dumpGCAttrs() { 336234287Sdim llvm::errs() << "\n################\n"; 337234287Sdim for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) { 338234287Sdim GCAttrOccurrence &Attr = GCAttrs[i]; 339234287Sdim llvm::errs() << "KIND: " 340234287Sdim << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak"); 341234287Sdim llvm::errs() << "\nLOC: "; 342234287Sdim Attr.Loc.dump(Pass.Ctx.getSourceManager()); 343234287Sdim llvm::errs() << "\nTYPE: "; 344234287Sdim Attr.ModifiedType.dump(); 345234287Sdim if (Attr.Dcl) { 346234287Sdim llvm::errs() << "DECL:\n"; 347234287Sdim Attr.Dcl->dump(); 348234287Sdim } else { 349234287Sdim llvm::errs() << "DECL: NONE"; 350234287Sdim } 351234287Sdim llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; 352234287Sdim llvm::errs() << "\n----------------\n"; 353234287Sdim } 354234287Sdim llvm::errs() << "\n################\n"; 355234287Sdim} 356