1234287Sdim//===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim
10234287Sdim#include "Transforms.h"
11234287Sdim#include "Internals.h"
12245431Sdim#include "clang/AST/ASTContext.h"
13245431Sdim#include "clang/Basic/SourceManager.h"
14234287Sdim#include "clang/Lex/Lexer.h"
15234287Sdim#include "clang/Sema/SemaDiagnostic.h"
16234287Sdim#include "llvm/ADT/SmallString.h"
17234287Sdim#include "llvm/ADT/TinyPtrVector.h"
18245431Sdim#include "llvm/Support/SaveAndRestore.h"
19234287Sdim
20234287Sdimusing namespace clang;
21234287Sdimusing namespace arcmt;
22234287Sdimusing namespace trans;
23234287Sdim
24234287Sdimnamespace {
25234287Sdim
26234287Sdim/// \brief Collects all the places where GC attributes __strong/__weak occur.
27234287Sdimclass GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
28234287Sdim  MigrationContext &MigrateCtx;
29234287Sdim  bool FullyMigratable;
30234287Sdim  std::vector<ObjCPropertyDecl *> &AllProps;
31234287Sdim
32234287Sdim  typedef RecursiveASTVisitor<GCAttrsCollector> base;
33234287Sdimpublic:
34234287Sdim  GCAttrsCollector(MigrationContext &ctx,
35234287Sdim                   std::vector<ObjCPropertyDecl *> &AllProps)
36234287Sdim    : MigrateCtx(ctx), FullyMigratable(false),
37234287Sdim      AllProps(AllProps) { }
38234287Sdim
39234287Sdim  bool shouldWalkTypesOfTypeLocs() const { return false; }
40234287Sdim
41234287Sdim  bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
42234287Sdim    handleAttr(TL);
43234287Sdim    return true;
44234287Sdim  }
45234287Sdim
46234287Sdim  bool TraverseDecl(Decl *D) {
47234287Sdim    if (!D || D->isImplicit())
48234287Sdim      return true;
49234287Sdim
50234287Sdim    SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D));
51234287Sdim
52234287Sdim    if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) {
53234287Sdim      lookForAttribute(PropD, PropD->getTypeSourceInfo());
54234287Sdim      AllProps.push_back(PropD);
55234287Sdim    } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
56234287Sdim      lookForAttribute(DD, DD->getTypeSourceInfo());
57234287Sdim    }
58234287Sdim    return base::TraverseDecl(D);
59234287Sdim  }
60234287Sdim
61234287Sdim  void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) {
62234287Sdim    if (!TInfo)
63234287Sdim      return;
64234287Sdim    TypeLoc TL = TInfo->getTypeLoc();
65234287Sdim    while (TL) {
66252723Sdim      if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) {
67252723Sdim        TL = QL.getUnqualifiedLoc();
68252723Sdim      } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) {
69252723Sdim        if (handleAttr(Attr, D))
70234287Sdim          break;
71252723Sdim        TL = Attr.getModifiedLoc();
72252723Sdim      } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
73252723Sdim        TL = Arr.getElementLoc();
74252723Sdim      } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
75252723Sdim        TL = PT.getPointeeLoc();
76252723Sdim      } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
77252723Sdim        TL = RT.getPointeeLoc();
78234287Sdim      else
79234287Sdim        break;
80234287Sdim    }
81234287Sdim  }
82234287Sdim
83234287Sdim  bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) {
84234287Sdim    if (TL.getAttrKind() != AttributedType::attr_objc_ownership)
85234287Sdim      return false;
86234287Sdim
87234287Sdim    SourceLocation Loc = TL.getAttrNameLoc();
88234287Sdim    unsigned RawLoc = Loc.getRawEncoding();
89234287Sdim    if (MigrateCtx.AttrSet.count(RawLoc))
90234287Sdim      return true;
91234287Sdim
92234287Sdim    ASTContext &Ctx = MigrateCtx.Pass.Ctx;
93234287Sdim    SourceManager &SM = Ctx.getSourceManager();
94234287Sdim    if (Loc.isMacroID())
95234287Sdim      Loc = SM.getImmediateExpansionRange(Loc).first;
96234287Sdim    SmallString<32> Buf;
97234287Sdim    bool Invalid = false;
98234287Sdim    StringRef Spell = Lexer::getSpelling(
99234287Sdim                                  SM.getSpellingLoc(TL.getAttrEnumOperandLoc()),
100234287Sdim                                  Buf, SM, Ctx.getLangOpts(), &Invalid);
101234287Sdim    if (Invalid)
102234287Sdim      return false;
103234287Sdim    MigrationContext::GCAttrOccurrence::AttrKind Kind;
104234287Sdim    if (Spell == "strong")
105234287Sdim      Kind = MigrationContext::GCAttrOccurrence::Strong;
106234287Sdim    else if (Spell == "weak")
107234287Sdim      Kind = MigrationContext::GCAttrOccurrence::Weak;
108234287Sdim    else
109234287Sdim      return false;
110234287Sdim
111234287Sdim    MigrateCtx.AttrSet.insert(RawLoc);
112234287Sdim    MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
113234287Sdim    MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
114234287Sdim
115234287Sdim    Attr.Kind = Kind;
116234287Sdim    Attr.Loc = Loc;
117234287Sdim    Attr.ModifiedType = TL.getModifiedLoc().getType();
118234287Sdim    Attr.Dcl = D;
119234287Sdim    Attr.FullyMigratable = FullyMigratable;
120234287Sdim    return true;
121234287Sdim  }
122234287Sdim
123234287Sdim  bool isMigratable(Decl *D) {
124234287Sdim    if (isa<TranslationUnitDecl>(D))
125234287Sdim      return false;
126234287Sdim
127234287Sdim    if (isInMainFile(D))
128234287Sdim      return true;
129234287Sdim
130234287Sdim    if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
131234287Sdim      return FD->hasBody();
132234287Sdim
133234287Sdim    if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
134234287Sdim      return hasObjCImpl(ContD);
135234287Sdim
136234287Sdim    if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
137234287Sdim      for (CXXRecordDecl::method_iterator
138234287Sdim             MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) {
139245431Sdim        if (MI->isOutOfLine())
140234287Sdim          return true;
141234287Sdim      }
142234287Sdim      return false;
143234287Sdim    }
144234287Sdim
145234287Sdim    return isMigratable(cast<Decl>(D->getDeclContext()));
146234287Sdim  }
147234287Sdim
148234287Sdim  static bool hasObjCImpl(Decl *D) {
149234287Sdim    if (!D)
150234287Sdim      return false;
151234287Sdim    if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
152234287Sdim      if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
153234287Sdim        return ID->getImplementation() != 0;
154234287Sdim      if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
155234287Sdim        return CD->getImplementation() != 0;
156234287Sdim      if (isa<ObjCImplDecl>(ContD))
157234287Sdim        return true;
158234287Sdim      return false;
159234287Sdim    }
160234287Sdim    return false;
161234287Sdim  }
162234287Sdim
163234287Sdim  bool isInMainFile(Decl *D) {
164234287Sdim    if (!D)
165234287Sdim      return false;
166234287Sdim
167234287Sdim    for (Decl::redecl_iterator
168234287Sdim           I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I)
169245431Sdim      if (!isInMainFile(I->getLocation()))
170234287Sdim        return false;
171234287Sdim
172234287Sdim    return true;
173234287Sdim  }
174234287Sdim
175234287Sdim  bool isInMainFile(SourceLocation Loc) {
176234287Sdim    if (Loc.isInvalid())
177234287Sdim      return false;
178234287Sdim
179234287Sdim    SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
180234287Sdim    return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
181234287Sdim  }
182234287Sdim};
183234287Sdim
184234287Sdim} // anonymous namespace
185234287Sdim
186234287Sdimstatic void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
187234287Sdim  TransformActions &TA = MigrateCtx.Pass.TA;
188234287Sdim
189234287Sdim  for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
190234287Sdim    MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
191234287Sdim    if (Attr.FullyMigratable && Attr.Dcl) {
192234287Sdim      if (Attr.ModifiedType.isNull())
193234287Sdim        continue;
194234287Sdim      if (!Attr.ModifiedType->isObjCRetainableType()) {
195234287Sdim        TA.reportError("GC managed memory will become unmanaged in ARC",
196234287Sdim                       Attr.Loc);
197234287Sdim      }
198234287Sdim    }
199234287Sdim  }
200234287Sdim}
201234287Sdim
202234287Sdimstatic void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
203234287Sdim  TransformActions &TA = MigrateCtx.Pass.TA;
204234287Sdim
205234287Sdim  for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
206234287Sdim    MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
207234287Sdim    if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
208234287Sdim      if (Attr.ModifiedType.isNull() ||
209234287Sdim          !Attr.ModifiedType->isObjCRetainableType())
210234287Sdim        continue;
211234287Sdim      if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
212234287Sdim                        /*AllowOnUnknownClass=*/true)) {
213234287Sdim        Transaction Trans(TA);
214234287Sdim        if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
215234287Sdim          TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
216234287Sdim        TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
217234287Sdim                           diag::err_arc_unsupported_weak_class,
218234287Sdim                           Attr.Loc);
219234287Sdim      }
220234287Sdim    }
221234287Sdim  }
222234287Sdim}
223234287Sdim
224234287Sdimtypedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
225234287Sdim
226234287Sdimstatic void checkAllAtProps(MigrationContext &MigrateCtx,
227234287Sdim                            SourceLocation AtLoc,
228234287Sdim                            IndivPropsTy &IndProps) {
229234287Sdim  if (IndProps.empty())
230234287Sdim    return;
231234287Sdim
232234287Sdim  for (IndivPropsTy::iterator
233234287Sdim         PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
234234287Sdim    QualType T = (*PI)->getType();
235234287Sdim    if (T.isNull() || !T->isObjCRetainableType())
236234287Sdim      return;
237234287Sdim  }
238234287Sdim
239234287Sdim  SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
240234287Sdim  bool hasWeak = false, hasStrong = false;
241234287Sdim  ObjCPropertyDecl::PropertyAttributeKind
242234287Sdim    Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
243234287Sdim  for (IndivPropsTy::iterator
244234287Sdim         PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
245234287Sdim    ObjCPropertyDecl *PD = *PI;
246234287Sdim    Attrs = PD->getPropertyAttributesAsWritten();
247234287Sdim    TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
248234287Sdim    if (!TInfo)
249234287Sdim      return;
250234287Sdim    TypeLoc TL = TInfo->getTypeLoc();
251252723Sdim    if (AttributedTypeLoc ATL =
252252723Sdim            TL.getAs<AttributedTypeLoc>()) {
253252723Sdim      ATLs.push_back(std::make_pair(ATL, PD));
254234287Sdim      if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
255234287Sdim        hasWeak = true;
256234287Sdim      } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
257234287Sdim        hasStrong = true;
258234287Sdim      else
259234287Sdim        return;
260234287Sdim    }
261234287Sdim  }
262234287Sdim  if (ATLs.empty())
263234287Sdim    return;
264234287Sdim  if (hasWeak && hasStrong)
265234287Sdim    return;
266234287Sdim
267234287Sdim  TransformActions &TA = MigrateCtx.Pass.TA;
268234287Sdim  Transaction Trans(TA);
269234287Sdim
270234287Sdim  if (GCAttrsCollector::hasObjCImpl(
271234287Sdim                              cast<Decl>(IndProps.front()->getDeclContext()))) {
272234287Sdim    if (hasWeak)
273234287Sdim      MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
274234287Sdim
275234287Sdim  } else {
276234287Sdim    StringRef toAttr = "strong";
277234287Sdim    if (hasWeak) {
278234287Sdim      if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
279234287Sdim                       /*AllowOnUnkwownClass=*/true))
280234287Sdim        toAttr = "weak";
281234287Sdim      else
282234287Sdim        toAttr = "unsafe_unretained";
283234287Sdim    }
284234287Sdim    if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
285234287Sdim      MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
286234287Sdim    else
287234287Sdim      MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
288234287Sdim  }
289234287Sdim
290234287Sdim  for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
291234287Sdim    SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
292234287Sdim    if (Loc.isMacroID())
293234287Sdim      Loc = MigrateCtx.Pass.Ctx.getSourceManager()
294234287Sdim                                         .getImmediateExpansionRange(Loc).first;
295234287Sdim    TA.remove(Loc);
296234287Sdim    TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
297234287Sdim    TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
298234287Sdim                       ATLs[i].second->getLocation());
299234287Sdim    MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
300234287Sdim  }
301234287Sdim}
302234287Sdim
303234287Sdimstatic void checkAllProps(MigrationContext &MigrateCtx,
304234287Sdim                          std::vector<ObjCPropertyDecl *> &AllProps) {
305234287Sdim  typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
306234287Sdim  llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
307234287Sdim
308234287Sdim  for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
309234287Sdim    ObjCPropertyDecl *PD = AllProps[i];
310234287Sdim    if (PD->getPropertyAttributesAsWritten() &
311234287Sdim          (ObjCPropertyDecl::OBJC_PR_assign |
312234287Sdim           ObjCPropertyDecl::OBJC_PR_readonly)) {
313234287Sdim      SourceLocation AtLoc = PD->getAtLoc();
314234287Sdim      if (AtLoc.isInvalid())
315234287Sdim        continue;
316234287Sdim      unsigned RawAt = AtLoc.getRawEncoding();
317234287Sdim      AtProps[RawAt].push_back(PD);
318234287Sdim    }
319234287Sdim  }
320234287Sdim
321234287Sdim  for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
322234287Sdim         I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
323234287Sdim    SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
324234287Sdim    IndivPropsTy &IndProps = I->second;
325234287Sdim    checkAllAtProps(MigrateCtx, AtLoc, IndProps);
326234287Sdim  }
327234287Sdim}
328234287Sdim
329234287Sdimvoid GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
330234287Sdim  std::vector<ObjCPropertyDecl *> AllProps;
331234287Sdim  GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
332234287Sdim                                  MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
333234287Sdim
334234287Sdim  errorForGCAttrsOnNonObjC(MigrateCtx);
335234287Sdim  checkAllProps(MigrateCtx, AllProps);
336234287Sdim  checkWeakGCAttrs(MigrateCtx);
337234287Sdim}
338234287Sdim
339234287Sdimvoid MigrationContext::dumpGCAttrs() {
340234287Sdim  llvm::errs() << "\n################\n";
341234287Sdim  for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
342234287Sdim    GCAttrOccurrence &Attr = GCAttrs[i];
343234287Sdim    llvm::errs() << "KIND: "
344234287Sdim        << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
345234287Sdim    llvm::errs() << "\nLOC: ";
346234287Sdim    Attr.Loc.dump(Pass.Ctx.getSourceManager());
347234287Sdim    llvm::errs() << "\nTYPE: ";
348234287Sdim    Attr.ModifiedType.dump();
349234287Sdim    if (Attr.Dcl) {
350234287Sdim      llvm::errs() << "DECL:\n";
351234287Sdim      Attr.Dcl->dump();
352234287Sdim    } else {
353234287Sdim      llvm::errs() << "DECL: NONE";
354234287Sdim    }
355234287Sdim    llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
356234287Sdim    llvm::errs() << "\n----------------\n";
357234287Sdim  }
358234287Sdim  llvm::errs() << "\n################\n";
359234287Sdim}
360