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