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