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