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