ObjCMT.cpp revision 2a4ebcfe8b5920ca35924359135f3c5ddca7b171
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 "clang/ARCMigrate/ARCMTActions.h"
11#include "clang/AST/ASTConsumer.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/NSAPI.h"
14#include "clang/AST/ParentMap.h"
15#include "clang/AST/RecursiveASTVisitor.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Edit/Commit.h"
18#include "clang/Edit/EditedSource.h"
19#include "clang/Edit/EditsReceiver.h"
20#include "clang/Edit/Rewriters.h"
21#include "clang/Frontend/CompilerInstance.h"
22#include "clang/Frontend/MultiplexConsumer.h"
23#include "clang/Lex/PPConditionalDirectiveRecord.h"
24#include "clang/Lex/Preprocessor.h"
25#include "clang/Rewrite/Core/Rewriter.h"
26#include "llvm/ADT/SmallString.h"
27
28using namespace clang;
29using namespace arcmt;
30
31namespace {
32
33class ObjCMigrateASTConsumer : public ASTConsumer {
34  void migrateDecl(Decl *D);
35  void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
36
37public:
38  std::string MigrateDir;
39  bool MigrateLiterals;
40  bool MigrateSubscripting;
41  OwningPtr<NSAPI> NSAPIObj;
42  OwningPtr<edit::EditedSource> Editor;
43  FileRemapper &Remapper;
44  FileManager &FileMgr;
45  const PPConditionalDirectiveRecord *PPRec;
46  Preprocessor &PP;
47  bool IsOutputFile;
48
49  ObjCMigrateASTConsumer(StringRef migrateDir,
50                         bool migrateLiterals,
51                         bool migrateSubscripting,
52                         FileRemapper &remapper,
53                         FileManager &fileMgr,
54                         const PPConditionalDirectiveRecord *PPRec,
55                         Preprocessor &PP,
56                         bool isOutputFile = false)
57  : MigrateDir(migrateDir),
58    MigrateLiterals(migrateLiterals),
59    MigrateSubscripting(migrateSubscripting),
60    Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
61    IsOutputFile(isOutputFile) { }
62
63protected:
64  virtual void Initialize(ASTContext &Context) {
65    NSAPIObj.reset(new NSAPI(Context));
66    Editor.reset(new edit::EditedSource(Context.getSourceManager(),
67                                        Context.getLangOpts(),
68                                        PPRec));
69  }
70
71  virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
72    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
73      migrateDecl(*I);
74    return true;
75  }
76  virtual void HandleInterestingDecl(DeclGroupRef DG) {
77    // Ignore decls from the PCH.
78  }
79  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
80    ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
81  }
82
83  virtual void HandleTranslationUnit(ASTContext &Ctx);
84};
85
86}
87
88ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
89                             StringRef migrateDir,
90                             bool migrateLiterals,
91                             bool migrateSubscripting)
92  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
93    MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
94    CompInst(0) {
95  if (MigrateDir.empty())
96    MigrateDir = "."; // user current directory if none is given.
97}
98
99ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
100                                                  StringRef InFile) {
101  PPConditionalDirectiveRecord *
102    PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
103  CompInst->getPreprocessor().addPPCallbacks(PPRec);
104  ASTConsumer *
105    WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
106  ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
107                                                       MigrateLiterals,
108                                                       MigrateSubscripting,
109                                                       Remapper,
110                                                    CompInst->getFileManager(),
111                                                       PPRec,
112                                                       CompInst->getPreprocessor());
113  ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
114  return new MultiplexConsumer(Consumers);
115}
116
117bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
118  Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
119                        /*ignoreIfFilesChanges=*/true);
120  CompInst = &CI;
121  CI.getDiagnostics().setIgnoreAllWarnings(true);
122  return true;
123}
124
125namespace {
126class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
127  ObjCMigrateASTConsumer &Consumer;
128  ParentMap &PMap;
129
130public:
131  ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
132    : Consumer(consumer), PMap(PMap) { }
133
134  bool shouldVisitTemplateInstantiations() const { return false; }
135  bool shouldWalkTypesOfTypeLocs() const { return false; }
136
137  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
138    if (Consumer.MigrateLiterals) {
139      edit::Commit commit(*Consumer.Editor);
140      edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
141      Consumer.Editor->commit(commit);
142    }
143
144    if (Consumer.MigrateSubscripting) {
145      edit::Commit commit(*Consumer.Editor);
146      edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
147      Consumer.Editor->commit(commit);
148    }
149
150    return true;
151  }
152
153  bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
154    // Do depth first; we want to rewrite the subexpressions first so that if
155    // we have to move expressions we will move them already rewritten.
156    for (Stmt::child_range range = E->children(); range; ++range)
157      if (!TraverseStmt(*range))
158        return false;
159
160    return WalkUpFromObjCMessageExpr(E);
161  }
162};
163
164class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
165  ObjCMigrateASTConsumer &Consumer;
166  OwningPtr<ParentMap> PMap;
167
168public:
169  BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
170
171  bool shouldVisitTemplateInstantiations() const { return false; }
172  bool shouldWalkTypesOfTypeLocs() const { return false; }
173
174  bool TraverseStmt(Stmt *S) {
175    PMap.reset(new ParentMap(S));
176    ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
177    return true;
178  }
179};
180}
181
182void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
183  if (!D)
184    return;
185  if (isa<ObjCMethodDecl>(D))
186    return; // Wait for the ObjC container declaration.
187
188  BodyMigrator(*this).TraverseDecl(D);
189}
190
191void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
192                                                      ObjCInterfaceDecl *D) {
193  for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
194       M != MEnd; ++M) {
195    ObjCMethodDecl *Method = (*M);
196    if (Method->isPropertyAccessor() ||  Method->param_size() != 0)
197      continue;
198    // Is this method candidate to be a getter?
199    QualType GRT = Method->getResultType();
200    if (GRT->isVoidType())
201      continue;
202    // FIXME. Don't know what todo with attributes, skip for now.
203    if (Method->hasAttrs())
204      continue;
205
206    Selector GetterSelector = Method->getSelector();
207    IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
208    Selector SetterSelector =
209      SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
210                                             PP.getSelectorTable(),
211                                             getterName);
212    if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) {
213      // Is this a valid setter, matching the target getter?
214      QualType SRT = SetterMethod->getResultType();
215      if (!SRT->isVoidType())
216        continue;
217      const ParmVarDecl *argDecl = *SetterMethod->param_begin();
218      QualType ArgType = argDecl->getType();
219      if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
220          SetterMethod->hasAttrs())
221          continue;
222        edit::Commit commit(*Editor);
223        edit::rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit);
224        Editor->commit(commit);
225      }
226  }
227}
228
229namespace {
230
231class RewritesReceiver : public edit::EditsReceiver {
232  Rewriter &Rewrite;
233
234public:
235  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
236
237  virtual void insert(SourceLocation loc, StringRef text) {
238    Rewrite.InsertText(loc, text);
239  }
240  virtual void replace(CharSourceRange range, StringRef text) {
241    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
242  }
243};
244
245}
246
247void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
248
249  TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
250  for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
251       D != DEnd; ++D) {
252    if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
253      migrateObjCInterfaceDecl(Ctx, CDecl);
254  }
255
256  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
257  RewritesReceiver Rec(rewriter);
258  Editor->applyRewrites(Rec);
259
260  for (Rewriter::buffer_iterator
261        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
262    FileID FID = I->first;
263    RewriteBuffer &buf = I->second;
264    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
265    assert(file);
266    SmallString<512> newText;
267    llvm::raw_svector_ostream vecOS(newText);
268    buf.write(vecOS);
269    vecOS.flush();
270    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
271                   StringRef(newText.data(), newText.size()), file->getName());
272    SmallString<64> filePath(file->getName());
273    FileMgr.FixupRelativePath(filePath);
274    Remapper.remap(filePath.str(), memBuf);
275  }
276
277  if (IsOutputFile) {
278    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
279  } else {
280    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
281  }
282}
283
284bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
285  CI.getDiagnostics().setIgnoreAllWarnings(true);
286  return true;
287}
288
289ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
290                                                  StringRef InFile) {
291  PPConditionalDirectiveRecord *
292    PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
293  CI.getPreprocessor().addPPCallbacks(PPRec);
294  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
295                                    /*MigrateLiterals=*/true,
296                                    /*MigrateSubscripting=*/true,
297                                    Remapper,
298                                    CI.getFileManager(),
299                                    PPRec,
300                                    CI.getPreprocessor(),
301                                    /*isOutputFile=*/true);
302}
303