1//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
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 "clang/ARCMigrate/ARCMTActions.h"
12#include "clang/AST/ASTConsumer.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/NSAPI.h"
15#include "clang/AST/ParentMap.h"
16#include "clang/AST/RecursiveASTVisitor.h"
17#include "clang/Basic/FileManager.h"
18#include "clang/Edit/Commit.h"
19#include "clang/Edit/EditedSource.h"
20#include "clang/Edit/EditsReceiver.h"
21#include "clang/Edit/Rewriters.h"
22#include "clang/Frontend/CompilerInstance.h"
23#include "clang/Frontend/MultiplexConsumer.h"
24#include "clang/Lex/PPConditionalDirectiveRecord.h"
25#include "clang/Lex/Preprocessor.h"
26#include "clang/Rewrite/Core/Rewriter.h"
27#include "llvm/ADT/SmallString.h"
28
29using namespace clang;
30using namespace arcmt;
31
32namespace {
33
34class ObjCMigrateASTConsumer : public ASTConsumer {
35  void migrateDecl(Decl *D);
36  void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
37  void migrateProtocolConformance(ASTContext &Ctx,
38                                  const ObjCImplementationDecl *ImpDecl);
39  void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
40                     const TypedefDecl *TypedefDcl);
41  void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
42  void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
43                                 ObjCMethodDecl *OM);
44  void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
45                            ObjCMethodDecl *OM,
46                            ObjCInstanceTypeFamily OIT_Family = OIT_None);
47
48public:
49  std::string MigrateDir;
50  bool MigrateLiterals;
51  bool MigrateSubscripting;
52  bool MigrateProperty;
53  OwningPtr<NSAPI> NSAPIObj;
54  OwningPtr<edit::EditedSource> Editor;
55  FileRemapper &Remapper;
56  FileManager &FileMgr;
57  const PPConditionalDirectiveRecord *PPRec;
58  Preprocessor &PP;
59  bool IsOutputFile;
60  llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
61
62  ObjCMigrateASTConsumer(StringRef migrateDir,
63                         bool migrateLiterals,
64                         bool migrateSubscripting,
65                         bool migrateProperty,
66                         FileRemapper &remapper,
67                         FileManager &fileMgr,
68                         const PPConditionalDirectiveRecord *PPRec,
69                         Preprocessor &PP,
70                         bool isOutputFile = false)
71  : MigrateDir(migrateDir),
72    MigrateLiterals(migrateLiterals),
73    MigrateSubscripting(migrateSubscripting),
74    MigrateProperty(migrateProperty),
75    Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
76    IsOutputFile(isOutputFile) { }
77
78protected:
79  virtual void Initialize(ASTContext &Context) {
80    NSAPIObj.reset(new NSAPI(Context));
81    Editor.reset(new edit::EditedSource(Context.getSourceManager(),
82                                        Context.getLangOpts(),
83                                        PPRec));
84  }
85
86  virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
87    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
88      migrateDecl(*I);
89    return true;
90  }
91  virtual void HandleInterestingDecl(DeclGroupRef DG) {
92    // Ignore decls from the PCH.
93  }
94  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
95    ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
96  }
97
98  virtual void HandleTranslationUnit(ASTContext &Ctx);
99};
100
101}
102
103ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
104                             StringRef migrateDir,
105                             bool migrateLiterals,
106                             bool migrateSubscripting,
107                             bool migrateProperty)
108  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
109    MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
110    MigrateProperty(migrateProperty),
111    CompInst(0) {
112  if (MigrateDir.empty())
113    MigrateDir = "."; // user current directory if none is given.
114}
115
116ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
117                                                  StringRef InFile) {
118  PPConditionalDirectiveRecord *
119    PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
120  CompInst->getPreprocessor().addPPCallbacks(PPRec);
121  ASTConsumer *
122    WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
123  ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
124                                                       MigrateLiterals,
125                                                       MigrateSubscripting,
126                                                       MigrateProperty,
127                                                       Remapper,
128                                                    CompInst->getFileManager(),
129                                                       PPRec,
130                                                       CompInst->getPreprocessor());
131  ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
132  return new MultiplexConsumer(Consumers);
133}
134
135bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
136  Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
137                        /*ignoreIfFilesChanges=*/true);
138  CompInst = &CI;
139  CI.getDiagnostics().setIgnoreAllWarnings(true);
140  return true;
141}
142
143namespace {
144class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
145  ObjCMigrateASTConsumer &Consumer;
146  ParentMap &PMap;
147
148public:
149  ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
150    : Consumer(consumer), PMap(PMap) { }
151
152  bool shouldVisitTemplateInstantiations() const { return false; }
153  bool shouldWalkTypesOfTypeLocs() const { return false; }
154
155  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
156    if (Consumer.MigrateLiterals) {
157      edit::Commit commit(*Consumer.Editor);
158      edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
159      Consumer.Editor->commit(commit);
160    }
161
162    if (Consumer.MigrateSubscripting) {
163      edit::Commit commit(*Consumer.Editor);
164      edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
165      Consumer.Editor->commit(commit);
166    }
167
168    return true;
169  }
170
171  bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
172    // Do depth first; we want to rewrite the subexpressions first so that if
173    // we have to move expressions we will move them already rewritten.
174    for (Stmt::child_range range = E->children(); range; ++range)
175      if (!TraverseStmt(*range))
176        return false;
177
178    return WalkUpFromObjCMessageExpr(E);
179  }
180};
181
182class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
183  ObjCMigrateASTConsumer &Consumer;
184  OwningPtr<ParentMap> PMap;
185
186public:
187  BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
188
189  bool shouldVisitTemplateInstantiations() const { return false; }
190  bool shouldWalkTypesOfTypeLocs() const { return false; }
191
192  bool TraverseStmt(Stmt *S) {
193    PMap.reset(new ParentMap(S));
194    ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
195    return true;
196  }
197};
198}
199
200void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
201  if (!D)
202    return;
203  if (isa<ObjCMethodDecl>(D))
204    return; // Wait for the ObjC container declaration.
205
206  BodyMigrator(*this).TraverseDecl(D);
207}
208
209static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter,
210                                  const ObjCMethodDecl *Setter,
211                                  const NSAPI &NS, edit::Commit &commit) {
212  ASTContext &Context = NS.getASTContext();
213  std::string PropertyString = "@property";
214
215  std::string PropertyNameString = Getter->getNameAsString();
216  StringRef PropertyName(PropertyNameString);
217  // Short circuit properties that contain the name "delegate" or "dataSource",
218  // or have exact name "target" to have unsafe_unretained attribute.
219  if (PropertyName.equals("target") ||
220      (PropertyName.find("delegate") != StringRef::npos) ||
221      (PropertyName.find("dataSource") != StringRef::npos))
222    PropertyString += "(unsafe_unretained)";
223  else {
224    const ParmVarDecl *argDecl = *Setter->param_begin();
225    QualType ArgType = Context.getCanonicalType(argDecl->getType());
226    Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
227    bool RetainableObject = ArgType->isObjCRetainableType();
228    if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) {
229      if (const ObjCObjectPointerType *ObjPtrTy =
230          ArgType->getAs<ObjCObjectPointerType>()) {
231        ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
232        if (IDecl &&
233            IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
234          PropertyString += "(copy)";
235        else
236          PropertyString += "(retain)";
237      }
238    } else if (propertyLifetime == Qualifiers::OCL_Weak)
239      // TODO. More precise determination of 'weak' attribute requires
240      // looking into setter's implementation for backing weak ivar.
241      PropertyString += "(weak)";
242    else if (RetainableObject)
243      PropertyString += "(retain)";
244  }
245
246  // strip off any ARC lifetime qualifier.
247  QualType CanResultTy = Context.getCanonicalType(Getter->getResultType());
248  if (CanResultTy.getQualifiers().hasObjCLifetime()) {
249    Qualifiers Qs = CanResultTy.getQualifiers();
250    Qs.removeObjCLifetime();
251    CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
252  }
253  PropertyString += " ";
254  PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy());
255  PropertyString += " ";
256  PropertyString += PropertyNameString;
257  commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
258                                               Getter->getDeclaratorEndLoc()),
259                 PropertyString);
260  SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
261  // Get location past ';'
262  EndLoc = EndLoc.getLocWithOffset(1);
263  commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
264  return true;
265}
266
267void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
268                                                      ObjCInterfaceDecl *D) {
269  for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
270       M != MEnd; ++M) {
271    ObjCMethodDecl *Method = (*M);
272    if (Method->isPropertyAccessor() ||  Method->param_size() != 0)
273      continue;
274    // Is this method candidate to be a getter?
275    QualType GRT = Method->getResultType();
276    if (GRT->isVoidType())
277      continue;
278    // FIXME. Don't know what todo with attributes, skip for now.
279    if (Method->hasAttrs())
280      continue;
281
282    Selector GetterSelector = Method->getSelector();
283    IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
284    Selector SetterSelector =
285      SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
286                                             PP.getSelectorTable(),
287                                             getterName);
288    if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) {
289      // Is this a valid setter, matching the target getter?
290      QualType SRT = SetterMethod->getResultType();
291      if (!SRT->isVoidType())
292        continue;
293      const ParmVarDecl *argDecl = *SetterMethod->param_begin();
294      QualType ArgType = argDecl->getType();
295      if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
296          SetterMethod->hasAttrs())
297          continue;
298        edit::Commit commit(*Editor);
299        rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit);
300        Editor->commit(commit);
301      }
302  }
303}
304
305static bool
306ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
307                                      const ObjCImplementationDecl *ImpDecl,
308                                       const ObjCInterfaceDecl *IDecl,
309                                      ObjCProtocolDecl *Protocol) {
310  // In auto-synthesis, protocol properties are not synthesized. So,
311  // a conforming protocol must have its required properties declared
312  // in class interface.
313  bool HasAtleastOneRequiredProperty = false;
314  if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
315    for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
316         E = PDecl->prop_end(); P != E; ++P) {
317      ObjCPropertyDecl *Property = *P;
318      if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
319        continue;
320      HasAtleastOneRequiredProperty = true;
321      DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
322      if (R.size() == 0) {
323        // Relax the rule and look into class's implementation for a synthesize
324        // or dynamic declaration. Class is implementing a property coming from
325        // another protocol. This still makes the target protocol as conforming.
326        if (!ImpDecl->FindPropertyImplDecl(
327                                  Property->getDeclName().getAsIdentifierInfo()))
328          return false;
329      }
330      else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
331          if ((ClassProperty->getPropertyAttributes()
332              != Property->getPropertyAttributes()) ||
333              !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
334            return false;
335      }
336      else
337        return false;
338    }
339
340  // At this point, all required properties in this protocol conform to those
341  // declared in the class.
342  // Check that class implements the required methods of the protocol too.
343  bool HasAtleastOneRequiredMethod = false;
344  if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
345    if (PDecl->meth_begin() == PDecl->meth_end())
346      return HasAtleastOneRequiredProperty;
347    for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
348         MEnd = PDecl->meth_end(); M != MEnd; ++M) {
349      ObjCMethodDecl *MD = (*M);
350      if (MD->isImplicit())
351        continue;
352      if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
353        continue;
354      DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
355      if (R.size() == 0)
356        return false;
357      bool match = false;
358      HasAtleastOneRequiredMethod = true;
359      for (unsigned I = 0, N = R.size(); I != N; ++I)
360        if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
361          if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
362            match = true;
363            break;
364          }
365      if (!match)
366        return false;
367    }
368  }
369  if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
370    return true;
371  return false;
372}
373
374static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
375                    llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
376                    const NSAPI &NS, edit::Commit &commit) {
377  const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
378  std::string ClassString;
379  SourceLocation EndLoc =
380  IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
381
382  if (Protocols.empty()) {
383    ClassString = '<';
384    for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
385      ClassString += ConformingProtocols[i]->getNameAsString();
386      if (i != (e-1))
387        ClassString += ", ";
388    }
389    ClassString += "> ";
390  }
391  else {
392    ClassString = ", ";
393    for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
394      ClassString += ConformingProtocols[i]->getNameAsString();
395      if (i != (e-1))
396        ClassString += ", ";
397    }
398    ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
399    EndLoc = *PL;
400  }
401
402  commit.insertAfterToken(EndLoc, ClassString);
403  return true;
404}
405
406static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
407                                const TypedefDecl *TypedefDcl,
408                                const NSAPI &NS, edit::Commit &commit,
409                                bool IsNSIntegerType) {
410  std::string ClassString =
411    IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
412  ClassString += TypedefDcl->getIdentifier()->getName();
413  ClassString += ')';
414  SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
415  commit.replace(R, ClassString);
416  SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
417  EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
418  if (!EndOfTypedefLoc.isInvalid()) {
419    commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
420    return true;
421  }
422  return false;
423}
424
425static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
426                                const TypedefDecl *TypedefDcl,
427                                const NSAPI &NS, edit::Commit &commit,
428                                 bool IsNSIntegerType) {
429  std::string ClassString =
430    IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
431  ClassString += TypedefDcl->getIdentifier()->getName();
432  ClassString += ')';
433  SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
434  commit.replace(R, ClassString);
435  SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
436  commit.remove(SourceRange(TypedefLoc, TypedefLoc));
437  return true;
438}
439
440static bool UseNSOptionsMacro(ASTContext &Ctx,
441                              const EnumDecl *EnumDcl) {
442  bool PowerOfTwo = true;
443  for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
444       EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
445    EnumConstantDecl *Enumerator = (*EI);
446    const Expr *InitExpr = Enumerator->getInitExpr();
447    if (!InitExpr) {
448      PowerOfTwo = false;
449      continue;
450    }
451    InitExpr = InitExpr->IgnoreImpCasts();
452    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
453      if (BO->isShiftOp() || BO->isBitwiseOp())
454        return true;
455
456    uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
457    if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal))
458      PowerOfTwo = false;
459  }
460  return PowerOfTwo;
461}
462
463void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
464                                            const ObjCImplementationDecl *ImpDecl) {
465  const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
466  if (!IDecl || ObjCProtocolDecls.empty())
467    return;
468  // Find all implicit conforming protocols for this class
469  // and make them explicit.
470  llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
471  Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
472  llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
473
474  for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
475       ObjCProtocolDecls.begin(),
476       E = ObjCProtocolDecls.end(); I != E; ++I)
477    if (!ExplicitProtocols.count(*I))
478      PotentialImplicitProtocols.push_back(*I);
479
480  if (PotentialImplicitProtocols.empty())
481    return;
482
483  // go through list of non-optional methods and properties in each protocol
484  // in the PotentialImplicitProtocols list. If class implements every one of the
485  // methods and properties, then this class conforms to this protocol.
486  llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
487  for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
488    if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
489                                              PotentialImplicitProtocols[i]))
490      ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
491
492  if (ConformingProtocols.empty())
493    return;
494
495  // Further reduce number of conforming protocols. If protocol P1 is in the list
496  // protocol P2 (P2<P1>), No need to include P1.
497  llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
498  for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
499    bool DropIt = false;
500    ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
501    for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
502      ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
503      if (PDecl == TargetPDecl)
504        continue;
505      if (PDecl->lookupProtocolNamed(
506            TargetPDecl->getDeclName().getAsIdentifierInfo())) {
507        DropIt = true;
508        break;
509      }
510    }
511    if (!DropIt)
512      MinimalConformingProtocols.push_back(TargetPDecl);
513  }
514  edit::Commit commit(*Editor);
515  rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
516                             *NSAPIObj, commit);
517  Editor->commit(commit);
518}
519
520void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
521                                           const EnumDecl *EnumDcl,
522                                           const TypedefDecl *TypedefDcl) {
523  if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
524      !TypedefDcl->getIdentifier())
525    return;
526
527  QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
528  bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
529  bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
530
531  if (!IsNSIntegerType && !IsNSUIntegerType) {
532    // Also check for typedef enum {...} TD;
533    if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
534      if (EnumTy->getDecl() == EnumDcl) {
535        bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
536        if (NSOptions) {
537          if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
538            return;
539        }
540        else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
541          return;
542        edit::Commit commit(*Editor);
543        rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
544        Editor->commit(commit);
545      }
546    }
547    return;
548  }
549  if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
550    // We may still use NS_OPTIONS based on what we find in the enumertor list.
551    IsNSIntegerType = false;
552    IsNSUIntegerType = true;
553  }
554
555  // NS_ENUM must be available.
556  if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
557    return;
558  // NS_OPTIONS must be available.
559  if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
560    return;
561  edit::Commit commit(*Editor);
562  rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
563  Editor->commit(commit);
564}
565
566static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
567                                    ObjCMethodDecl *OM) {
568  SourceRange R;
569  std::string ClassString;
570  if (TypeSourceInfo *TSInfo =  OM->getResultTypeSourceInfo()) {
571    TypeLoc TL = TSInfo->getTypeLoc();
572    R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
573    ClassString = "instancetype";
574  }
575  else {
576    R = SourceRange(OM->getLocStart(), OM->getLocStart());
577    ClassString = OM->isInstanceMethod() ? '-' : '+';
578    ClassString += " (instancetype)";
579  }
580  edit::Commit commit(*ASTC.Editor);
581  commit.replace(R, ClassString);
582  ASTC.Editor->commit(commit);
583}
584
585void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
586                                                       ObjCContainerDecl *CDecl,
587                                                       ObjCMethodDecl *OM) {
588  ObjCInstanceTypeFamily OIT_Family =
589    Selector::getInstTypeMethodFamily(OM->getSelector());
590
591  std::string ClassName;
592  switch (OIT_Family) {
593    case OIT_None:
594      migrateFactoryMethod(Ctx, CDecl, OM);
595      return;
596    case OIT_Array:
597      ClassName = "NSArray";
598      break;
599    case OIT_Dictionary:
600      ClassName = "NSDictionary";
601      break;
602    case OIT_MemManage:
603      ClassName = "NSObject";
604      break;
605    case OIT_Singleton:
606      migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
607      return;
608  }
609  if (!OM->getResultType()->isObjCIdType())
610    return;
611
612  ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
613  if (!IDecl) {
614    if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
615      IDecl = CatDecl->getClassInterface();
616    else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
617      IDecl = ImpDecl->getClassInterface();
618  }
619  if (!IDecl ||
620      !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
621    migrateFactoryMethod(Ctx, CDecl, OM);
622    return;
623  }
624  ReplaceWithInstancetype(*this, OM);
625}
626
627void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
628                                                 ObjCContainerDecl *CDecl) {
629  // migrate methods which can have instancetype as their result type.
630  for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
631       MEnd = CDecl->meth_end();
632       M != MEnd; ++M) {
633    ObjCMethodDecl *Method = (*M);
634    migrateMethodInstanceType(Ctx, CDecl, Method);
635  }
636}
637
638void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
639                                                  ObjCContainerDecl *CDecl,
640                                                  ObjCMethodDecl *OM,
641                                                  ObjCInstanceTypeFamily OIT_Family) {
642  if (OM->isInstanceMethod() ||
643      OM->getResultType() == Ctx.getObjCInstanceType() ||
644      !OM->getResultType()->isObjCIdType())
645    return;
646
647  // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
648  // NSYYYNamE with matching names be at least 3 characters long.
649  ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
650  if (!IDecl) {
651    if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
652      IDecl = CatDecl->getClassInterface();
653    else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
654      IDecl = ImpDecl->getClassInterface();
655  }
656  if (!IDecl)
657    return;
658
659  std::string StringClassName = IDecl->getName();
660  StringRef LoweredClassName(StringClassName);
661  std::string StringLoweredClassName = LoweredClassName.lower();
662  LoweredClassName = StringLoweredClassName;
663
664  IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
665  std::string MethodName = MethodIdName->getName();
666  if (OIT_Family == OIT_Singleton) {
667    StringRef STRefMethodName(MethodName);
668    size_t len = 0;
669    if (STRefMethodName.startswith("standard"))
670      len = strlen("standard");
671    else if (STRefMethodName.startswith("shared"))
672      len = strlen("shared");
673    else if (STRefMethodName.startswith("default"))
674      len = strlen("default");
675    else
676      return;
677    MethodName = STRefMethodName.substr(len);
678  }
679  std::string MethodNameSubStr = MethodName.substr(0, 3);
680  StringRef MethodNamePrefix(MethodNameSubStr);
681  std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
682  MethodNamePrefix = StringLoweredMethodNamePrefix;
683  size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
684  if (Ix == StringRef::npos)
685    return;
686  std::string ClassNamePostfix = LoweredClassName.substr(Ix);
687  StringRef LoweredMethodName(MethodName);
688  std::string StringLoweredMethodName = LoweredMethodName.lower();
689  LoweredMethodName = StringLoweredMethodName;
690  if (!LoweredMethodName.startswith(ClassNamePostfix))
691    return;
692  ReplaceWithInstancetype(*this, OM);
693}
694
695namespace {
696
697class RewritesReceiver : public edit::EditsReceiver {
698  Rewriter &Rewrite;
699
700public:
701  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
702
703  virtual void insert(SourceLocation loc, StringRef text) {
704    Rewrite.InsertText(loc, text);
705  }
706  virtual void replace(CharSourceRange range, StringRef text) {
707    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
708  }
709};
710
711}
712
713void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
714
715  TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
716  if (MigrateProperty)
717    for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
718         D != DEnd; ++D) {
719      if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
720        migrateObjCInterfaceDecl(Ctx, CDecl);
721      else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
722        ObjCProtocolDecls.insert(PDecl);
723      else if (const ObjCImplementationDecl *ImpDecl =
724               dyn_cast<ObjCImplementationDecl>(*D))
725        migrateProtocolConformance(Ctx, ImpDecl);
726      else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
727        DeclContext::decl_iterator N = D;
728        ++N;
729        if (N != DEnd)
730          if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
731            migrateNSEnumDecl(Ctx, ED, TD);
732      }
733      // migrate methods which can have instancetype as their result type.
734      if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
735        migrateInstanceType(Ctx, CDecl);
736    }
737
738  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
739  RewritesReceiver Rec(rewriter);
740  Editor->applyRewrites(Rec);
741
742  for (Rewriter::buffer_iterator
743        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
744    FileID FID = I->first;
745    RewriteBuffer &buf = I->second;
746    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
747    assert(file);
748    SmallString<512> newText;
749    llvm::raw_svector_ostream vecOS(newText);
750    buf.write(vecOS);
751    vecOS.flush();
752    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
753                   StringRef(newText.data(), newText.size()), file->getName());
754    SmallString<64> filePath(file->getName());
755    FileMgr.FixupRelativePath(filePath);
756    Remapper.remap(filePath.str(), memBuf);
757  }
758
759  if (IsOutputFile) {
760    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
761  } else {
762    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
763  }
764}
765
766bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
767  CI.getDiagnostics().setIgnoreAllWarnings(true);
768  return true;
769}
770
771ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
772                                                  StringRef InFile) {
773  PPConditionalDirectiveRecord *
774    PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
775  CI.getPreprocessor().addPPCallbacks(PPRec);
776  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
777                                    /*MigrateLiterals=*/true,
778                                    /*MigrateSubscripting=*/true,
779                                    /*MigrateProperty*/true,
780                                    Remapper,
781                                    CI.getFileManager(),
782                                    PPRec,
783                                    CI.getPreprocessor(),
784                                    /*isOutputFile=*/true);
785}
786