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
19using namespace clang;
20using namespace arcmt;
21using namespace trans;
22
23namespace {
24
25/// Collects all the places where GC attributes __strong/__weak occur.
26class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
27  MigrationContext &MigrateCtx;
28  bool FullyMigratable;
29  std::vector<ObjCPropertyDecl *> &AllProps;
30
31  typedef RecursiveASTVisitor<GCAttrsCollector> base;
32public:
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
179static 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
195static 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
217typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
218
219static 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
297static 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
323void 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
333void 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