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