Clang Project

clang_source_code/lib/ARCMigrate/TransGCAttrs.cpp
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<GCAttrsCollectorbase;
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<boolSave(FullyMigratable, isMigratable(D));
50
51    if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) {
52      lookForAttribute(PropDPropD->getTypeSourceInfo());
53      AllProps.push_back(PropD);
54    } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
55      lookForAttribute(DDDD->getTypeSourceInfo());
56    }
57    return base::TraverseDecl(D);
58  }
59
60  void lookForAttribute(Decl *DTypeSourceInfo *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(AttrD))
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 TLDecl *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
176static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
177  TransformActions &TA = MigrateCtx.Pass.TA;
178
179  for (unsigned i = 0e = 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
192static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
193  TransformActions &TA = MigrateCtx.Pass.TA;
194
195  for (unsigned i = 0e = 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.CtxAttr.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
214typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
215
216static 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 = falsehasStrong = 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 = 0e = 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
294static 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 = 0e = 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
320void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
321  std::vector<ObjCPropertyDecl *> AllProps;
322  GCAttrsCollector(MigrateCtxAllProps).TraverseDecl(
323                                  MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
324
325  errorForGCAttrsOnNonObjC(MigrateCtx);
326  checkAllProps(MigrateCtxAllProps);
327  checkWeakGCAttrs(MigrateCtx);
328}
329
330void MigrationContext::dumpGCAttrs() {
331  llvm::errs() << "\n################\n";
332  for (unsigned i = 0e = 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
clang::arcmt::trans::GCAttrsTraverser::traverseTU
clang::arcmt::trans::MigrationContext::dumpGCAttrs