ObjCMT.cpp revision 8d3794e06a63578093bd71c3c2520bd01e6197a3
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  const ParmVarDecl *argDecl = *Setter->param_begin();
215  QualType ArgType = Context.getCanonicalType(argDecl->getType());
216  Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
217
218  if (ArgType->isObjCRetainableType() &&
219      propertyLifetime == Qualifiers::OCL_Strong) {
220    if (const ObjCObjectPointerType *ObjPtrTy =
221        ArgType->getAs<ObjCObjectPointerType>()) {
222      ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
223      if (IDecl &&
224          IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
225        PropertyString += "(copy)";
226    }
227  }
228  else if (propertyLifetime == Qualifiers::OCL_Weak)
229    // TODO. More precise determination of 'weak' attribute requires
230    // looking into setter's implementation for backing weak ivar.
231    PropertyString += "(weak)";
232  else
233    PropertyString += "(unsafe_unretained)";
234
235  // strip off any ARC lifetime qualifier.
236  QualType CanResultTy = Context.getCanonicalType(Getter->getResultType());
237  if (CanResultTy.getQualifiers().hasObjCLifetime()) {
238    Qualifiers Qs = CanResultTy.getQualifiers();
239    Qs.removeObjCLifetime();
240    CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
241  }
242  PropertyString += " ";
243  PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy());
244  PropertyString += " ";
245  PropertyString += Getter->getNameAsString();
246  commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
247                                               Getter->getDeclaratorEndLoc()),
248                 PropertyString);
249  SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
250  // Get location past ';'
251  EndLoc = EndLoc.getLocWithOffset(1);
252  commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
253  return true;
254}
255
256void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
257                                                      ObjCInterfaceDecl *D) {
258  for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
259       M != MEnd; ++M) {
260    ObjCMethodDecl *Method = (*M);
261    if (Method->isPropertyAccessor() ||  Method->param_size() != 0)
262      continue;
263    // Is this method candidate to be a getter?
264    QualType GRT = Method->getResultType();
265    if (GRT->isVoidType())
266      continue;
267    // FIXME. Don't know what todo with attributes, skip for now.
268    if (Method->hasAttrs())
269      continue;
270
271    Selector GetterSelector = Method->getSelector();
272    IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
273    Selector SetterSelector =
274      SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
275                                             PP.getSelectorTable(),
276                                             getterName);
277    if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) {
278      // Is this a valid setter, matching the target getter?
279      QualType SRT = SetterMethod->getResultType();
280      if (!SRT->isVoidType())
281        continue;
282      const ParmVarDecl *argDecl = *SetterMethod->param_begin();
283      QualType ArgType = argDecl->getType();
284      if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
285          SetterMethod->hasAttrs())
286          continue;
287        edit::Commit commit(*Editor);
288        rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit);
289        Editor->commit(commit);
290      }
291  }
292}
293
294static bool
295ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
296                                      const ObjCImplementationDecl *ImpDecl,
297                                       const ObjCInterfaceDecl *IDecl,
298                                      ObjCProtocolDecl *Protocol) {
299  // In auto-synthesis, protocol properties are not synthesized. So,
300  // a conforming protocol must have its required properties declared
301  // in class interface.
302  bool HasAtleastOneRequiredProperty = false;
303  if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
304    for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
305         E = PDecl->prop_end(); P != E; ++P) {
306      ObjCPropertyDecl *Property = *P;
307      if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
308        continue;
309      HasAtleastOneRequiredProperty = true;
310      DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
311      if (R.size() == 0) {
312        // Relax the rule and look into class's implementation for a synthesize
313        // or dynamic declaration. Class is implementing a property coming from
314        // another protocol. This still makes the target protocol as conforming.
315        if (!ImpDecl->FindPropertyImplDecl(
316                                  Property->getDeclName().getAsIdentifierInfo()))
317          return false;
318      }
319      else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
320          if ((ClassProperty->getPropertyAttributes()
321              != Property->getPropertyAttributes()) ||
322              !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
323            return false;
324      }
325      else
326        return false;
327    }
328
329  // At this point, all required properties in this protocol conform to those
330  // declared in the class.
331  // Check that class implements the required methods of the protocol too.
332  bool HasAtleastOneRequiredMethod = false;
333  if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
334    if (PDecl->meth_begin() == PDecl->meth_end())
335      return HasAtleastOneRequiredProperty;
336    for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
337         MEnd = PDecl->meth_end(); M != MEnd; ++M) {
338      ObjCMethodDecl *MD = (*M);
339      if (MD->isImplicit())
340        continue;
341      if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
342        continue;
343      DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
344      if (R.size() == 0)
345        return false;
346      bool match = false;
347      HasAtleastOneRequiredMethod = true;
348      for (unsigned I = 0, N = R.size(); I != N; ++I)
349        if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
350          if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
351            match = true;
352            break;
353          }
354      if (!match)
355        return false;
356    }
357  }
358  if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
359    return true;
360  return false;
361}
362
363static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
364                    llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
365                    const NSAPI &NS, edit::Commit &commit) {
366  const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
367  std::string ClassString;
368  SourceLocation EndLoc =
369  IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
370
371  if (Protocols.empty()) {
372    ClassString = '<';
373    for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
374      ClassString += ConformingProtocols[i]->getNameAsString();
375      if (i != (e-1))
376        ClassString += ", ";
377    }
378    ClassString += "> ";
379  }
380  else {
381    ClassString = ", ";
382    for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
383      ClassString += ConformingProtocols[i]->getNameAsString();
384      if (i != (e-1))
385        ClassString += ", ";
386    }
387    ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
388    EndLoc = *PL;
389  }
390
391  commit.insertAfterToken(EndLoc, ClassString);
392  return true;
393}
394
395static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
396                                const TypedefDecl *TypedefDcl,
397                                const NSAPI &NS, edit::Commit &commit,
398                                bool IsNSIntegerType) {
399  std::string ClassString =
400    IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
401  ClassString += TypedefDcl->getIdentifier()->getName();
402  ClassString += ')';
403  SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
404  commit.replace(R, ClassString);
405  SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
406  EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
407  if (!EndOfTypedefLoc.isInvalid()) {
408    commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
409    return true;
410  }
411  return false;
412}
413
414static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
415                                const TypedefDecl *TypedefDcl,
416                                const NSAPI &NS, edit::Commit &commit,
417                                 bool IsNSIntegerType) {
418  std::string ClassString =
419    IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
420  ClassString += TypedefDcl->getIdentifier()->getName();
421  ClassString += ')';
422  SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
423  commit.replace(R, ClassString);
424  SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
425  commit.remove(SourceRange(TypedefLoc, TypedefLoc));
426  return true;
427}
428
429static bool UseNSOptionsMacro(ASTContext &Ctx,
430                              const EnumDecl *EnumDcl) {
431  bool PowerOfTwo = true;
432  for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
433       EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
434    EnumConstantDecl *Enumerator = (*EI);
435    const Expr *InitExpr = Enumerator->getInitExpr();
436    if (!InitExpr) {
437      PowerOfTwo = false;
438      continue;
439    }
440    InitExpr = InitExpr->IgnoreImpCasts();
441    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
442      if (BO->isShiftOp() || BO->isBitwiseOp())
443        return true;
444
445    uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
446    if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal))
447      PowerOfTwo = false;
448  }
449  return PowerOfTwo;
450}
451
452void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
453                                            const ObjCImplementationDecl *ImpDecl) {
454  const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
455  if (!IDecl || ObjCProtocolDecls.empty())
456    return;
457  // Find all implicit conforming protocols for this class
458  // and make them explicit.
459  llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
460  Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
461  llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
462
463  for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
464       ObjCProtocolDecls.begin(),
465       E = ObjCProtocolDecls.end(); I != E; ++I)
466    if (!ExplicitProtocols.count(*I))
467      PotentialImplicitProtocols.push_back(*I);
468
469  if (PotentialImplicitProtocols.empty())
470    return;
471
472  // go through list of non-optional methods and properties in each protocol
473  // in the PotentialImplicitProtocols list. If class implements every one of the
474  // methods and properties, then this class conforms to this protocol.
475  llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
476  for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
477    if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
478                                              PotentialImplicitProtocols[i]))
479      ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
480
481  if (ConformingProtocols.empty())
482    return;
483
484  // Further reduce number of conforming protocols. If protocol P1 is in the list
485  // protocol P2 (P2<P1>), No need to include P1.
486  llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
487  for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
488    bool DropIt = false;
489    ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
490    for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
491      ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
492      if (PDecl == TargetPDecl)
493        continue;
494      if (PDecl->lookupProtocolNamed(
495            TargetPDecl->getDeclName().getAsIdentifierInfo())) {
496        DropIt = true;
497        break;
498      }
499    }
500    if (!DropIt)
501      MinimalConformingProtocols.push_back(TargetPDecl);
502  }
503  edit::Commit commit(*Editor);
504  rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
505                             *NSAPIObj, commit);
506  Editor->commit(commit);
507}
508
509void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
510                                           const EnumDecl *EnumDcl,
511                                           const TypedefDecl *TypedefDcl) {
512  if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
513      !TypedefDcl->getIdentifier())
514    return;
515
516  QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
517  bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
518  bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
519
520  if (!IsNSIntegerType && !IsNSUIntegerType) {
521    // Also check for typedef enum {...} TD;
522    if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
523      if (EnumTy->getDecl() == EnumDcl) {
524        bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
525        if (NSOptions) {
526          if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
527            return;
528        }
529        else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
530          return;
531        edit::Commit commit(*Editor);
532        rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
533        Editor->commit(commit);
534      }
535    }
536    return;
537  }
538  if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
539    // We may still use NS_OPTIONS based on what we find in the enumertor list.
540    IsNSIntegerType = false;
541    IsNSUIntegerType = true;
542  }
543
544  // NS_ENUM must be available.
545  if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
546    return;
547  // NS_OPTIONS must be available.
548  if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
549    return;
550  edit::Commit commit(*Editor);
551  rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
552  Editor->commit(commit);
553}
554
555static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
556                                    ObjCMethodDecl *OM) {
557  SourceRange R;
558  std::string ClassString;
559  if (TypeSourceInfo *TSInfo =  OM->getResultTypeSourceInfo()) {
560    TypeLoc TL = TSInfo->getTypeLoc();
561    R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
562    ClassString = "instancetype";
563  }
564  else {
565    R = SourceRange(OM->getLocStart(), OM->getLocStart());
566    ClassString = OM->isInstanceMethod() ? '-' : '+';
567    ClassString += " (instancetype)";
568  }
569  edit::Commit commit(*ASTC.Editor);
570  commit.replace(R, ClassString);
571  ASTC.Editor->commit(commit);
572}
573
574void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
575                                                       ObjCContainerDecl *CDecl,
576                                                       ObjCMethodDecl *OM) {
577  ObjCInstanceTypeFamily OIT_Family =
578    Selector::getInstTypeMethodFamily(OM->getSelector());
579
580  std::string ClassName;
581  switch (OIT_Family) {
582    case OIT_None:
583      migrateFactoryMethod(Ctx, CDecl, OM);
584      return;
585    case OIT_Array:
586      ClassName = "NSArray";
587      break;
588    case OIT_Dictionary:
589      ClassName = "NSDictionary";
590      break;
591    case OIT_MemManage:
592      ClassName = "NSObject";
593      break;
594    case OIT_Singleton:
595      migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
596      return;
597  }
598  if (!OM->getResultType()->isObjCIdType())
599    return;
600
601  ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
602  if (!IDecl) {
603    if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
604      IDecl = CatDecl->getClassInterface();
605    else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
606      IDecl = ImpDecl->getClassInterface();
607  }
608  if (!IDecl ||
609      !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
610    migrateFactoryMethod(Ctx, CDecl, OM);
611    return;
612  }
613  ReplaceWithInstancetype(*this, OM);
614}
615
616void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
617                                                 ObjCContainerDecl *CDecl) {
618  // migrate methods which can have instancetype as their result type.
619  for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
620       MEnd = CDecl->meth_end();
621       M != MEnd; ++M) {
622    ObjCMethodDecl *Method = (*M);
623    migrateMethodInstanceType(Ctx, CDecl, Method);
624  }
625}
626
627void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
628                                                  ObjCContainerDecl *CDecl,
629                                                  ObjCMethodDecl *OM,
630                                                  ObjCInstanceTypeFamily OIT_Family) {
631  if (OM->isInstanceMethod() || !OM->getResultType()->isObjCIdType())
632    return;
633
634  // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
635  // NSYYYNamE with matching names be at least 3 characters long.
636  ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
637  if (!IDecl) {
638    if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
639      IDecl = CatDecl->getClassInterface();
640    else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
641      IDecl = ImpDecl->getClassInterface();
642  }
643  if (!IDecl)
644    return;
645
646  std::string StringClassName = IDecl->getName();
647  StringRef LoweredClassName(StringClassName);
648  std::string StringLoweredClassName = LoweredClassName.lower();
649  LoweredClassName = StringLoweredClassName;
650
651  IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
652  std::string MethodName = MethodIdName->getName();
653  if (OIT_Family == OIT_Singleton) {
654    StringRef STRefMethodName(MethodName);
655    size_t len = 0;
656    if (STRefMethodName.startswith("standard"))
657      len = strlen("standard");
658    else if (STRefMethodName.startswith("shared"))
659      len = strlen("shared");
660    else if (STRefMethodName.startswith("default"))
661      len = strlen("default");
662    else
663      return;
664    MethodName = STRefMethodName.substr(len);
665  }
666  std::string MethodNameSubStr = MethodName.substr(0, 3);
667  StringRef MethodNamePrefix(MethodNameSubStr);
668  std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
669  MethodNamePrefix = StringLoweredMethodNamePrefix;
670  size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
671  if (Ix == StringRef::npos)
672    return;
673  std::string ClassNamePostfix = LoweredClassName.substr(Ix);
674  StringRef LoweredMethodName(MethodName);
675  std::string StringLoweredMethodName = LoweredMethodName.lower();
676  LoweredMethodName = StringLoweredMethodName;
677  if (!LoweredMethodName.startswith(ClassNamePostfix))
678    return;
679  ReplaceWithInstancetype(*this, OM);
680}
681
682namespace {
683
684class RewritesReceiver : public edit::EditsReceiver {
685  Rewriter &Rewrite;
686
687public:
688  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
689
690  virtual void insert(SourceLocation loc, StringRef text) {
691    Rewrite.InsertText(loc, text);
692  }
693  virtual void replace(CharSourceRange range, StringRef text) {
694    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
695  }
696};
697
698}
699
700void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
701
702  TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
703  if (MigrateProperty)
704    for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
705         D != DEnd; ++D) {
706      if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
707        migrateObjCInterfaceDecl(Ctx, CDecl);
708      else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
709        ObjCProtocolDecls.insert(PDecl);
710      else if (const ObjCImplementationDecl *ImpDecl =
711               dyn_cast<ObjCImplementationDecl>(*D))
712        migrateProtocolConformance(Ctx, ImpDecl);
713      else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
714        DeclContext::decl_iterator N = D;
715        ++N;
716        if (N != DEnd)
717          if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
718            migrateNSEnumDecl(Ctx, ED, TD);
719      }
720      // migrate methods which can have instancetype as their result type.
721      if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
722        migrateInstanceType(Ctx, CDecl);
723    }
724
725  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
726  RewritesReceiver Rec(rewriter);
727  Editor->applyRewrites(Rec);
728
729  for (Rewriter::buffer_iterator
730        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
731    FileID FID = I->first;
732    RewriteBuffer &buf = I->second;
733    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
734    assert(file);
735    SmallString<512> newText;
736    llvm::raw_svector_ostream vecOS(newText);
737    buf.write(vecOS);
738    vecOS.flush();
739    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
740                   StringRef(newText.data(), newText.size()), file->getName());
741    SmallString<64> filePath(file->getName());
742    FileMgr.FixupRelativePath(filePath);
743    Remapper.remap(filePath.str(), memBuf);
744  }
745
746  if (IsOutputFile) {
747    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
748  } else {
749    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
750  }
751}
752
753bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
754  CI.getDiagnostics().setIgnoreAllWarnings(true);
755  return true;
756}
757
758ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
759                                                  StringRef InFile) {
760  PPConditionalDirectiveRecord *
761    PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
762  CI.getPreprocessor().addPPCallbacks(PPRec);
763  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
764                                    /*MigrateLiterals=*/true,
765                                    /*MigrateSubscripting=*/true,
766                                    /*MigrateProperty*/true,
767                                    Remapper,
768                                    CI.getFileManager(),
769                                    PPRec,
770                                    CI.getPreprocessor(),
771                                    /*isOutputFile=*/true);
772}
773