1234287Sdim//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
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 "clang/ARCMigrate/ARCMTActions.h"
11249423Sdim#include "clang/AST/ASTConsumer.h"
12239462Sdim#include "clang/AST/ASTContext.h"
13249423Sdim#include "clang/AST/NSAPI.h"
14249423Sdim#include "clang/AST/ParentMap.h"
15234287Sdim#include "clang/AST/RecursiveASTVisitor.h"
16249423Sdim#include "clang/Basic/FileManager.h"
17249423Sdim#include "clang/Edit/Commit.h"
18234287Sdim#include "clang/Edit/EditedSource.h"
19234287Sdim#include "clang/Edit/EditsReceiver.h"
20249423Sdim#include "clang/Edit/Rewriters.h"
21249423Sdim#include "clang/Frontend/CompilerInstance.h"
22249423Sdim#include "clang/Frontend/MultiplexConsumer.h"
23249423Sdim#include "clang/Lex/PPConditionalDirectiveRecord.h"
24249423Sdim#include "clang/Lex/Preprocessor.h"
25243830Sdim#include "clang/Rewrite/Core/Rewriter.h"
26234287Sdim#include "llvm/ADT/SmallString.h"
27234287Sdim
28234287Sdimusing namespace clang;
29234287Sdimusing namespace arcmt;
30234287Sdim
31234287Sdimnamespace {
32234287Sdim
33234287Sdimclass ObjCMigrateASTConsumer : public ASTConsumer {
34234287Sdim  void migrateDecl(Decl *D);
35234287Sdim
36234287Sdimpublic:
37234287Sdim  std::string MigrateDir;
38234287Sdim  bool MigrateLiterals;
39234287Sdim  bool MigrateSubscripting;
40249423Sdim  OwningPtr<NSAPI> NSAPIObj;
41249423Sdim  OwningPtr<edit::EditedSource> Editor;
42234287Sdim  FileRemapper &Remapper;
43234287Sdim  FileManager &FileMgr;
44249423Sdim  const PPConditionalDirectiveRecord *PPRec;
45234287Sdim  bool IsOutputFile;
46234287Sdim
47234287Sdim  ObjCMigrateASTConsumer(StringRef migrateDir,
48234287Sdim                         bool migrateLiterals,
49234287Sdim                         bool migrateSubscripting,
50234287Sdim                         FileRemapper &remapper,
51234287Sdim                         FileManager &fileMgr,
52249423Sdim                         const PPConditionalDirectiveRecord *PPRec,
53234287Sdim                         bool isOutputFile = false)
54234287Sdim  : MigrateDir(migrateDir),
55234287Sdim    MigrateLiterals(migrateLiterals),
56234287Sdim    MigrateSubscripting(migrateSubscripting),
57234287Sdim    Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec),
58234287Sdim    IsOutputFile(isOutputFile) { }
59234287Sdim
60234287Sdimprotected:
61234287Sdim  virtual void Initialize(ASTContext &Context) {
62234287Sdim    NSAPIObj.reset(new NSAPI(Context));
63234287Sdim    Editor.reset(new edit::EditedSource(Context.getSourceManager(),
64234287Sdim                                        Context.getLangOpts(),
65234287Sdim                                        PPRec));
66234287Sdim  }
67234287Sdim
68234287Sdim  virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
69234287Sdim    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
70234287Sdim      migrateDecl(*I);
71234287Sdim    return true;
72234287Sdim  }
73234287Sdim  virtual void HandleInterestingDecl(DeclGroupRef DG) {
74234287Sdim    // Ignore decls from the PCH.
75234287Sdim  }
76234287Sdim  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
77234287Sdim    ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
78234287Sdim  }
79234287Sdim
80234287Sdim  virtual void HandleTranslationUnit(ASTContext &Ctx);
81234287Sdim};
82234287Sdim
83234287Sdim}
84234287Sdim
85234287SdimObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
86234287Sdim                             StringRef migrateDir,
87234287Sdim                             bool migrateLiterals,
88234287Sdim                             bool migrateSubscripting)
89234287Sdim  : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
90234287Sdim    MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
91234287Sdim    CompInst(0) {
92234287Sdim  if (MigrateDir.empty())
93234287Sdim    MigrateDir = "."; // user current directory if none is given.
94234287Sdim}
95234287Sdim
96234287SdimASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
97234287Sdim                                                  StringRef InFile) {
98249423Sdim  PPConditionalDirectiveRecord *
99249423Sdim    PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
100249423Sdim  CompInst->getPreprocessor().addPPCallbacks(PPRec);
101234287Sdim  ASTConsumer *
102234287Sdim    WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
103234287Sdim  ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
104234287Sdim                                                       MigrateLiterals,
105234287Sdim                                                       MigrateSubscripting,
106234287Sdim                                                       Remapper,
107234287Sdim                                                    CompInst->getFileManager(),
108249423Sdim                                                       PPRec);
109234287Sdim  ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
110234287Sdim  return new MultiplexConsumer(Consumers);
111234287Sdim}
112234287Sdim
113234287Sdimbool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
114234287Sdim  Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
115234287Sdim                        /*ignoreIfFilesChanges=*/true);
116234287Sdim  CompInst = &CI;
117234287Sdim  CI.getDiagnostics().setIgnoreAllWarnings(true);
118234287Sdim  return true;
119234287Sdim}
120234287Sdim
121234287Sdimnamespace {
122234287Sdimclass ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
123234287Sdim  ObjCMigrateASTConsumer &Consumer;
124249423Sdim  ParentMap &PMap;
125234287Sdim
126234287Sdimpublic:
127249423Sdim  ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
128249423Sdim    : Consumer(consumer), PMap(PMap) { }
129234287Sdim
130234287Sdim  bool shouldVisitTemplateInstantiations() const { return false; }
131234287Sdim  bool shouldWalkTypesOfTypeLocs() const { return false; }
132234287Sdim
133234287Sdim  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
134234287Sdim    if (Consumer.MigrateLiterals) {
135234287Sdim      edit::Commit commit(*Consumer.Editor);
136249423Sdim      edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
137234287Sdim      Consumer.Editor->commit(commit);
138234287Sdim    }
139234287Sdim
140234287Sdim    if (Consumer.MigrateSubscripting) {
141234287Sdim      edit::Commit commit(*Consumer.Editor);
142234287Sdim      edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
143234287Sdim      Consumer.Editor->commit(commit);
144234287Sdim    }
145234287Sdim
146234287Sdim    return true;
147234287Sdim  }
148234287Sdim
149234287Sdim  bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
150234287Sdim    // Do depth first; we want to rewrite the subexpressions first so that if
151234287Sdim    // we have to move expressions we will move them already rewritten.
152234287Sdim    for (Stmt::child_range range = E->children(); range; ++range)
153234287Sdim      if (!TraverseStmt(*range))
154234287Sdim        return false;
155234287Sdim
156234287Sdim    return WalkUpFromObjCMessageExpr(E);
157234287Sdim  }
158234287Sdim};
159249423Sdim
160249423Sdimclass BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
161249423Sdim  ObjCMigrateASTConsumer &Consumer;
162249423Sdim  OwningPtr<ParentMap> PMap;
163249423Sdim
164249423Sdimpublic:
165249423Sdim  BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
166249423Sdim
167249423Sdim  bool shouldVisitTemplateInstantiations() const { return false; }
168249423Sdim  bool shouldWalkTypesOfTypeLocs() const { return false; }
169249423Sdim
170249423Sdim  bool TraverseStmt(Stmt *S) {
171249423Sdim    PMap.reset(new ParentMap(S));
172249423Sdim    ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
173249423Sdim    return true;
174249423Sdim  }
175249423Sdim};
176234287Sdim}
177234287Sdim
178234287Sdimvoid ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
179234287Sdim  if (!D)
180234287Sdim    return;
181234287Sdim  if (isa<ObjCMethodDecl>(D))
182234287Sdim    return; // Wait for the ObjC container declaration.
183234287Sdim
184249423Sdim  BodyMigrator(*this).TraverseDecl(D);
185234287Sdim}
186234287Sdim
187234287Sdimnamespace {
188234287Sdim
189234287Sdimclass RewritesReceiver : public edit::EditsReceiver {
190234287Sdim  Rewriter &Rewrite;
191234287Sdim
192234287Sdimpublic:
193234287Sdim  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
194234287Sdim
195234287Sdim  virtual void insert(SourceLocation loc, StringRef text) {
196234287Sdim    Rewrite.InsertText(loc, text);
197234287Sdim  }
198234287Sdim  virtual void replace(CharSourceRange range, StringRef text) {
199234287Sdim    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
200234287Sdim  }
201234287Sdim};
202234287Sdim
203234287Sdim}
204234287Sdim
205234287Sdimvoid ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
206234287Sdim  Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
207234287Sdim  RewritesReceiver Rec(rewriter);
208234287Sdim  Editor->applyRewrites(Rec);
209234287Sdim
210234287Sdim  for (Rewriter::buffer_iterator
211234287Sdim        I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
212234287Sdim    FileID FID = I->first;
213234287Sdim    RewriteBuffer &buf = I->second;
214234287Sdim    const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
215234287Sdim    assert(file);
216249423Sdim    SmallString<512> newText;
217234287Sdim    llvm::raw_svector_ostream vecOS(newText);
218234287Sdim    buf.write(vecOS);
219234287Sdim    vecOS.flush();
220234287Sdim    llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
221234287Sdim                   StringRef(newText.data(), newText.size()), file->getName());
222249423Sdim    SmallString<64> filePath(file->getName());
223234287Sdim    FileMgr.FixupRelativePath(filePath);
224234287Sdim    Remapper.remap(filePath.str(), memBuf);
225234287Sdim  }
226234287Sdim
227234287Sdim  if (IsOutputFile) {
228234287Sdim    Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
229234287Sdim  } else {
230234287Sdim    Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
231234287Sdim  }
232234287Sdim}
233234287Sdim
234234287Sdimbool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
235239462Sdim  CI.getDiagnostics().setIgnoreAllWarnings(true);
236234287Sdim  return true;
237234287Sdim}
238234287Sdim
239234287SdimASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
240234287Sdim                                                  StringRef InFile) {
241249423Sdim  PPConditionalDirectiveRecord *
242249423Sdim    PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
243249423Sdim  CI.getPreprocessor().addPPCallbacks(PPRec);
244234287Sdim  return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
245234287Sdim                                    /*MigrateLiterals=*/true,
246234287Sdim                                    /*MigrateSubscripting=*/true,
247234287Sdim                                    Remapper,
248234287Sdim                                    CI.getFileManager(),
249249423Sdim                                    PPRec,
250234287Sdim                                    /*isOutputFile=*/true);
251234287Sdim}
252