ObjCMT.cpp revision 55fc873017f10f6f566b182b70f6fc22aefa3464
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/RecursiveASTVisitor.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Edit/Commit.h"
17#include "clang/Edit/EditedSource.h"
18#include "clang/Edit/EditsReceiver.h"
19#include "clang/Edit/Rewriters.h"
20#include "clang/Frontend/CompilerInstance.h"
21#include "clang/Frontend/MultiplexConsumer.h"
22#include "clang/Lex/PPConditionalDirectiveRecord.h"
23#include "clang/Lex/Preprocessor.h"
24#include "clang/Rewrite/Core/Rewriter.h"
25#include "llvm/ADT/SmallString.h"
26
27using namespace clang;
28using namespace arcmt;
29
30namespace {
31
32class ObjCMigrateASTConsumer : public ASTConsumer {
33  void migrateDecl(Decl *D);
34
35public:
36  std::string MigrateDir;
37  bool MigrateLiterals;
38  bool MigrateSubscripting;
39  llvm::OwningPtr<NSAPI> NSAPIObj;
40  llvm::OwningPtr<edit::EditedSource> Editor;
41  FileRemapper &Remapper;
42  FileManager &FileMgr;
43  const PPConditionalDirectiveRecord *PPRec;
44  bool IsOutputFile;
45
46  ObjCMigrateASTConsumer(StringRef migrateDir,
47                         bool migrateLiterals,
48                         bool migrateSubscripting,
49                         FileRemapper &remapper,
50                         FileManager &fileMgr,
51                         const PPConditionalDirectiveRecord *PPRec,
52                         bool isOutputFile = false)
53  : MigrateDir(migrateDir),
54    MigrateLiterals(migrateLiterals),
55    MigrateSubscripting(migrateSubscripting),
56    Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec),
57    IsOutputFile(isOutputFile) { }
58
59protected:
60  virtual void Initialize(ASTContext &Context) {
61    NSAPIObj.reset(new NSAPI(Context));
62    Editor.reset(new edit::EditedSource(Context.getSourceManager(),
63                                        Context.getLangOpts(),
64                                        PPRec));
65  }
66
67  virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
68    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
69      migrateDecl(*I);
70    return true;
71  }
72  virtual void HandleInterestingDecl(DeclGroupRef DG) {
73    // Ignore decls from the PCH.
74  }
75  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
76    ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
77  }
78
79  virtual void HandleTranslationUnit(ASTContext &Ctx);
80};
81
82}
83
84ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
85                             StringRef migrateDir,
86                             bool migrateLiterals,
87                             bool migrateSubscripting)
88  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
89    MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
90    CompInst(0) {
91  if (MigrateDir.empty())
92    MigrateDir = "."; // user current directory if none is given.
93}
94
95ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
96                                                  StringRef InFile) {
97  PPConditionalDirectiveRecord *
98    PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
99  CompInst->getPreprocessor().addPPCallbacks(PPRec);
100  ASTConsumer *
101    WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
102  ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
103                                                       MigrateLiterals,
104                                                       MigrateSubscripting,
105                                                       Remapper,
106                                                    CompInst->getFileManager(),
107                                                       PPRec);
108  ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
109  return new MultiplexConsumer(Consumers);
110}
111
112bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
113  Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
114                        /*ignoreIfFilesChanges=*/true);
115  CompInst = &CI;
116  CI.getDiagnostics().setIgnoreAllWarnings(true);
117  return true;
118}
119
120namespace {
121class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
122  ObjCMigrateASTConsumer &Consumer;
123
124public:
125  ObjCMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
126
127  bool shouldVisitTemplateInstantiations() const { return false; }
128  bool shouldWalkTypesOfTypeLocs() const { return false; }
129
130  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
131    if (Consumer.MigrateLiterals) {
132      edit::Commit commit(*Consumer.Editor);
133      edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit);
134      Consumer.Editor->commit(commit);
135    }
136
137    if (Consumer.MigrateSubscripting) {
138      edit::Commit commit(*Consumer.Editor);
139      edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
140      Consumer.Editor->commit(commit);
141    }
142
143    return true;
144  }
145
146  bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
147    // Do depth first; we want to rewrite the subexpressions first so that if
148    // we have to move expressions we will move them already rewritten.
149    for (Stmt::child_range range = E->children(); range; ++range)
150      if (!TraverseStmt(*range))
151        return false;
152
153    return WalkUpFromObjCMessageExpr(E);
154  }
155};
156}
157
158void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
159  if (!D)
160    return;
161  if (isa<ObjCMethodDecl>(D))
162    return; // Wait for the ObjC container declaration.
163
164  ObjCMigrator(*this).TraverseDecl(D);
165}
166
167namespace {
168
169class RewritesReceiver : public edit::EditsReceiver {
170  Rewriter &Rewrite;
171
172public:
173  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
174
175  virtual void insert(SourceLocation loc, StringRef text) {
176    Rewrite.InsertText(loc, text);
177  }
178  virtual void replace(CharSourceRange range, StringRef text) {
179    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
180  }
181};
182
183}
184
185void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
186  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
187  RewritesReceiver Rec(rewriter);
188  Editor->applyRewrites(Rec);
189
190  for (Rewriter::buffer_iterator
191        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
192    FileID FID = I->first;
193    RewriteBuffer &buf = I->second;
194    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
195    assert(file);
196    llvm::SmallString<512> newText;
197    llvm::raw_svector_ostream vecOS(newText);
198    buf.write(vecOS);
199    vecOS.flush();
200    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
201                   StringRef(newText.data(), newText.size()), file->getName());
202    llvm::SmallString<64> filePath(file->getName());
203    FileMgr.FixupRelativePath(filePath);
204    Remapper.remap(filePath.str(), memBuf);
205  }
206
207  if (IsOutputFile) {
208    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
209  } else {
210    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
211  }
212}
213
214bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
215  CI.getDiagnostics().setIgnoreAllWarnings(true);
216  return true;
217}
218
219ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
220                                                  StringRef InFile) {
221  PPConditionalDirectiveRecord *
222    PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
223  CI.getPreprocessor().addPPCallbacks(PPRec);
224  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
225                                    /*MigrateLiterals=*/true,
226                                    /*MigrateSubscripting=*/true,
227                                    Remapper,
228                                    CI.getFileManager(),
229                                    PPRec,
230                                    /*isOutputFile=*/true);
231}
232