TransGCAttrs.cpp revision 234287
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/Lex/Lexer.h"
13#include "clang/Basic/SourceManager.h"
14#include "llvm/Support/SaveAndRestore.h"
15#include "clang/Sema/SemaDiagnostic.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/TinyPtrVector.h"
18
19using namespace clang;
20using namespace arcmt;
21using namespace trans;
22
23namespace {
24
25/// \brief 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 (const QualifiedTypeLoc *QL = dyn_cast<QualifiedTypeLoc>(&TL)) {
66        TL = QL->getUnqualifiedLoc();
67      } else if (const AttributedTypeLoc *
68                   Attr = dyn_cast<AttributedTypeLoc>(&TL)) {
69        if (handleAttr(*Attr, D))
70          break;
71        TL = Attr->getModifiedLoc();
72      } else if (const ArrayTypeLoc *Arr = dyn_cast<ArrayTypeLoc>(&TL)) {
73        TL = Arr->getElementLoc();
74      } else if (const PointerTypeLoc *PT = dyn_cast<PointerTypeLoc>(&TL)) {
75        TL = PT->getPointeeLoc();
76      } else if (const ReferenceTypeLoc *RT = dyn_cast<ReferenceTypeLoc>(&TL))
77        TL = RT->getPointeeLoc();
78      else
79        break;
80    }
81  }
82
83  bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) {
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 (CXXRecordDecl::method_iterator
138             MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) {
139        if ((*MI)->isOutOfLine())
140          return true;
141      }
142      return false;
143    }
144
145    return isMigratable(cast<Decl>(D->getDeclContext()));
146  }
147
148  static bool hasObjCImpl(Decl *D) {
149    if (!D)
150      return false;
151    if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
152      if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
153        return ID->getImplementation() != 0;
154      if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
155        return CD->getImplementation() != 0;
156      if (isa<ObjCImplDecl>(ContD))
157        return true;
158      return false;
159    }
160    return false;
161  }
162
163  bool isInMainFile(Decl *D) {
164    if (!D)
165      return false;
166
167    for (Decl::redecl_iterator
168           I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I)
169      if (!isInMainFile((*I)->getLocation()))
170        return false;
171
172    return true;
173  }
174
175  bool isInMainFile(SourceLocation Loc) {
176    if (Loc.isInvalid())
177      return false;
178
179    SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
180    return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
181  }
182};
183
184} // anonymous namespace
185
186static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
187  TransformActions &TA = MigrateCtx.Pass.TA;
188
189  for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
190    MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
191    if (Attr.FullyMigratable && Attr.Dcl) {
192      if (Attr.ModifiedType.isNull())
193        continue;
194      if (!Attr.ModifiedType->isObjCRetainableType()) {
195        TA.reportError("GC managed memory will become unmanaged in ARC",
196                       Attr.Loc);
197      }
198    }
199  }
200}
201
202static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
203  TransformActions &TA = MigrateCtx.Pass.TA;
204
205  for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
206    MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
207    if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
208      if (Attr.ModifiedType.isNull() ||
209          !Attr.ModifiedType->isObjCRetainableType())
210        continue;
211      if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
212                        /*AllowOnUnknownClass=*/true)) {
213        Transaction Trans(TA);
214        if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
215          TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
216        TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
217                           diag::err_arc_unsupported_weak_class,
218                           Attr.Loc);
219      }
220    }
221  }
222}
223
224typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
225
226static void checkAllAtProps(MigrationContext &MigrateCtx,
227                            SourceLocation AtLoc,
228                            IndivPropsTy &IndProps) {
229  if (IndProps.empty())
230    return;
231
232  for (IndivPropsTy::iterator
233         PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
234    QualType T = (*PI)->getType();
235    if (T.isNull() || !T->isObjCRetainableType())
236      return;
237  }
238
239  SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
240  bool hasWeak = false, hasStrong = false;
241  ObjCPropertyDecl::PropertyAttributeKind
242    Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
243  for (IndivPropsTy::iterator
244         PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
245    ObjCPropertyDecl *PD = *PI;
246    Attrs = PD->getPropertyAttributesAsWritten();
247    TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
248    if (!TInfo)
249      return;
250    TypeLoc TL = TInfo->getTypeLoc();
251    if (AttributedTypeLoc *ATL = dyn_cast<AttributedTypeLoc>(&TL)) {
252      ATLs.push_back(std::make_pair(*ATL, PD));
253      if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
254        hasWeak = true;
255      } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
256        hasStrong = true;
257      else
258        return;
259    }
260  }
261  if (ATLs.empty())
262    return;
263  if (hasWeak && hasStrong)
264    return;
265
266  TransformActions &TA = MigrateCtx.Pass.TA;
267  Transaction Trans(TA);
268
269  if (GCAttrsCollector::hasObjCImpl(
270                              cast<Decl>(IndProps.front()->getDeclContext()))) {
271    if (hasWeak)
272      MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
273
274  } else {
275    StringRef toAttr = "strong";
276    if (hasWeak) {
277      if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
278                       /*AllowOnUnkwownClass=*/true))
279        toAttr = "weak";
280      else
281        toAttr = "unsafe_unretained";
282    }
283    if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
284      MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
285    else
286      MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
287  }
288
289  for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
290    SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
291    if (Loc.isMacroID())
292      Loc = MigrateCtx.Pass.Ctx.getSourceManager()
293                                         .getImmediateExpansionRange(Loc).first;
294    TA.remove(Loc);
295    TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
296    TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
297                       ATLs[i].second->getLocation());
298    MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
299  }
300}
301
302static void checkAllProps(MigrationContext &MigrateCtx,
303                          std::vector<ObjCPropertyDecl *> &AllProps) {
304  typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
305  llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
306
307  for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
308    ObjCPropertyDecl *PD = AllProps[i];
309    if (PD->getPropertyAttributesAsWritten() &
310          (ObjCPropertyDecl::OBJC_PR_assign |
311           ObjCPropertyDecl::OBJC_PR_readonly)) {
312      SourceLocation AtLoc = PD->getAtLoc();
313      if (AtLoc.isInvalid())
314        continue;
315      unsigned RawAt = AtLoc.getRawEncoding();
316      AtProps[RawAt].push_back(PD);
317    }
318  }
319
320  for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
321         I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
322    SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
323    IndivPropsTy &IndProps = I->second;
324    checkAllAtProps(MigrateCtx, AtLoc, IndProps);
325  }
326}
327
328void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
329  std::vector<ObjCPropertyDecl *> AllProps;
330  GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
331                                  MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
332
333  errorForGCAttrsOnNonObjC(MigrateCtx);
334  checkAllProps(MigrateCtx, AllProps);
335  checkWeakGCAttrs(MigrateCtx);
336}
337
338void MigrationContext::dumpGCAttrs() {
339  llvm::errs() << "\n################\n";
340  for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
341    GCAttrOccurrence &Attr = GCAttrs[i];
342    llvm::errs() << "KIND: "
343        << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
344    llvm::errs() << "\nLOC: ";
345    Attr.Loc.dump(Pass.Ctx.getSourceManager());
346    llvm::errs() << "\nTYPE: ";
347    Attr.ModifiedType.dump();
348    if (Attr.Dcl) {
349      llvm::errs() << "DECL:\n";
350      Attr.Dcl->dump();
351    } else {
352      llvm::errs() << "DECL: NONE";
353    }
354    llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
355    llvm::errs() << "\n----------------\n";
356  }
357  llvm::errs() << "\n################\n";
358}
359