ASTUnit.cpp revision 4cc4433e4f8df9a5863b5bf66727a8494eaf5c63
1//===--- ASTUnit.cpp - ASTUnit utility ------------------------------------===// 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// ASTUnit Implementation. 11// 12//===----------------------------------------------------------------------===// 13 14#include "clang/Frontend/ASTUnit.h" 15#include "clang/Frontend/PCHReader.h" 16#include "clang/AST/ASTContext.h" 17#include "clang/AST/ASTConsumer.h" 18#include "clang/AST/DeclVisitor.h" 19#include "clang/AST/StmtVisitor.h" 20#include "clang/Driver/Compilation.h" 21#include "clang/Driver/Driver.h" 22#include "clang/Driver/Job.h" 23#include "clang/Driver/Tool.h" 24#include "clang/Frontend/CompilerInstance.h" 25#include "clang/Frontend/FrontendActions.h" 26#include "clang/Frontend/FrontendDiagnostic.h" 27#include "clang/Frontend/FrontendOptions.h" 28#include "clang/Lex/HeaderSearch.h" 29#include "clang/Lex/Preprocessor.h" 30#include "clang/Basic/TargetOptions.h" 31#include "clang/Basic/TargetInfo.h" 32#include "clang/Basic/Diagnostic.h" 33#include "llvm/Support/MemoryBuffer.h" 34#include "llvm/System/Host.h" 35#include "llvm/System/Path.h" 36using namespace clang; 37 38ASTUnit::ASTUnit(bool _MainFileIsAST) 39 : tempFile(false), MainFileIsAST(_MainFileIsAST) { 40} 41ASTUnit::~ASTUnit() { 42 if (tempFile) 43 llvm::sys::Path(getPCHFileName()).eraseFromDisk(); 44} 45 46namespace { 47 48/// \brief Gathers information from PCHReader that will be used to initialize 49/// a Preprocessor. 50class PCHInfoCollector : public PCHReaderListener { 51 LangOptions &LangOpt; 52 HeaderSearch &HSI; 53 std::string &TargetTriple; 54 std::string &Predefines; 55 unsigned &Counter; 56 57 unsigned NumHeaderInfos; 58 59public: 60 PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI, 61 std::string &TargetTriple, std::string &Predefines, 62 unsigned &Counter) 63 : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple), 64 Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {} 65 66 virtual bool ReadLanguageOptions(const LangOptions &LangOpts) { 67 LangOpt = LangOpts; 68 return false; 69 } 70 71 virtual bool ReadTargetTriple(llvm::StringRef Triple) { 72 TargetTriple = Triple; 73 return false; 74 } 75 76 virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, 77 FileID PCHBufferID, 78 llvm::StringRef OriginalFileName, 79 std::string &SuggestedPredefines) { 80 Predefines = PCHPredef; 81 return false; 82 } 83 84 virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) { 85 HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++); 86 } 87 88 virtual void ReadCounter(unsigned Value) { 89 Counter = Value; 90 } 91}; 92 93} // anonymous namespace 94 95const std::string &ASTUnit::getOriginalSourceFileName() { 96 return OriginalSourceFile; 97} 98 99const std::string &ASTUnit::getPCHFileName() { 100 assert(isMainFileAST() && "Not an ASTUnit from a PCH file!"); 101 return static_cast<PCHReader *>(Ctx->getExternalSource())->getFileName(); 102} 103 104ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename, 105 Diagnostic &Diags, 106 bool OnlyLocalDecls, 107 bool UseBumpAllocator, 108 RemappedFile *RemappedFiles, 109 unsigned NumRemappedFiles) { 110 llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true)); 111 AST->OnlyLocalDecls = OnlyLocalDecls; 112 AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager())); 113 114 for (unsigned I = 0; I != NumRemappedFiles; ++I) { 115 // Create the file entry for the file that we're mapping from. 116 const FileEntry *FromFile 117 = AST->getFileManager().getVirtualFile(RemappedFiles[I].first, 118 RemappedFiles[I].second->getBufferSize(), 119 0); 120 if (!FromFile) { 121 Diags.Report(diag::err_fe_remap_missing_from_file) 122 << RemappedFiles[I].first; 123 continue; 124 } 125 126 // Override the contents of the "from" file with the contents of 127 // the "to" file. 128 AST->getSourceManager().overrideFileContents(FromFile, 129 RemappedFiles[I].second); 130 } 131 132 // Gather Info for preprocessor construction later on. 133 134 LangOptions LangInfo; 135 HeaderSearch &HeaderInfo = *AST->HeaderInfo.get(); 136 std::string TargetTriple; 137 std::string Predefines; 138 unsigned Counter; 139 140 llvm::OwningPtr<PCHReader> Reader; 141 llvm::OwningPtr<ExternalASTSource> Source; 142 143 Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(), 144 Diags)); 145 Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple, 146 Predefines, Counter)); 147 148 switch (Reader->ReadPCH(Filename)) { 149 case PCHReader::Success: 150 break; 151 152 case PCHReader::Failure: 153 case PCHReader::IgnorePCH: 154 Diags.Report(diag::err_fe_unable_to_load_pch); 155 return NULL; 156 } 157 158 AST->OriginalSourceFile = Reader->getOriginalSourceFile(); 159 160 // PCH loaded successfully. Now create the preprocessor. 161 162 // Get information about the target being compiled for. 163 // 164 // FIXME: This is broken, we should store the TargetOptions in the PCH. 165 TargetOptions TargetOpts; 166 TargetOpts.ABI = ""; 167 TargetOpts.CPU = ""; 168 TargetOpts.Features.clear(); 169 TargetOpts.Triple = TargetTriple; 170 AST->Target.reset(TargetInfo::CreateTargetInfo(Diags, TargetOpts)); 171 AST->PP.reset(new Preprocessor(Diags, LangInfo, *AST->Target.get(), 172 AST->getSourceManager(), HeaderInfo)); 173 Preprocessor &PP = *AST->PP.get(); 174 175 PP.setPredefines(Reader->getSuggestedPredefines()); 176 PP.setCounterValue(Counter); 177 Reader->setPreprocessor(PP); 178 179 // Create and initialize the ASTContext. 180 181 AST->Ctx.reset(new ASTContext(LangInfo, 182 AST->getSourceManager(), 183 *AST->Target.get(), 184 PP.getIdentifierTable(), 185 PP.getSelectorTable(), 186 PP.getBuiltinInfo(), 187 /* FreeMemory = */ !UseBumpAllocator, 188 /* size_reserve = */0)); 189 ASTContext &Context = *AST->Ctx.get(); 190 191 Reader->InitializeContext(Context); 192 193 // Attach the PCH reader to the AST context as an external AST 194 // source, so that declarations will be deserialized from the 195 // PCH file as needed. 196 Source.reset(Reader.take()); 197 Context.setExternalSource(Source); 198 199 return AST.take(); 200} 201 202namespace { 203 204class TopLevelDeclTrackerConsumer : public ASTConsumer { 205 ASTUnit &Unit; 206 207public: 208 TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {} 209 210 void HandleTopLevelDecl(DeclGroupRef D) { 211 for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) 212 Unit.getTopLevelDecls().push_back(*it); 213 } 214}; 215 216class TopLevelDeclTrackerAction : public ASTFrontendAction { 217public: 218 ASTUnit &Unit; 219 220 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 221 llvm::StringRef InFile) { 222 return new TopLevelDeclTrackerConsumer(Unit); 223 } 224 225public: 226 TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} 227 228 virtual bool hasCodeCompletionSupport() const { return false; } 229}; 230 231} 232 233ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI, 234 Diagnostic &Diags, 235 bool OnlyLocalDecls) { 236 // Create the compiler instance to use for building the AST. 237 CompilerInstance Clang; 238 llvm::OwningPtr<ASTUnit> AST; 239 llvm::OwningPtr<TopLevelDeclTrackerAction> Act; 240 241 Clang.setInvocation(const_cast<CompilerInvocation*>(&CI)); 242 243 Clang.setDiagnostics(&Diags); 244 Clang.setDiagnosticClient(Diags.getClient()); 245 246 // Create the target instance. 247 Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), 248 Clang.getTargetOpts())); 249 if (!Clang.hasTarget()) 250 goto error; 251 252 // Inform the target of the language options. 253 // 254 // FIXME: We shouldn't need to do this, the target should be immutable once 255 // created. This complexity should be lifted elsewhere. 256 Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); 257 258 assert(Clang.getFrontendOpts().Inputs.size() == 1 && 259 "Invocation must have exactly one source file!"); 260 assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST && 261 "FIXME: AST inputs not yet supported here!"); 262 263 // Create the AST unit. 264 AST.reset(new ASTUnit(false)); 265 266 AST->OnlyLocalDecls = OnlyLocalDecls; 267 AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; 268 269 // Create a file manager object to provide access to and cache the filesystem. 270 Clang.setFileManager(&AST->getFileManager()); 271 272 // Create the source manager. 273 Clang.setSourceManager(&AST->getSourceManager()); 274 275 // Create the preprocessor. 276 Clang.createPreprocessor(); 277 278 Act.reset(new TopLevelDeclTrackerAction(*AST)); 279 if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, 280 /*IsAST=*/false)) 281 goto error; 282 283 Act->Execute(); 284 285 // Steal the created target, context, and preprocessor, and take back the 286 // source and file managers. 287 AST->Ctx.reset(Clang.takeASTContext()); 288 AST->PP.reset(Clang.takePreprocessor()); 289 Clang.takeSourceManager(); 290 Clang.takeFileManager(); 291 AST->Target.reset(Clang.takeTarget()); 292 293 Act->EndSourceFile(); 294 295 Clang.takeDiagnosticClient(); 296 Clang.takeDiagnostics(); 297 Clang.takeInvocation(); 298 299 return AST.take(); 300 301error: 302 Clang.takeSourceManager(); 303 Clang.takeFileManager(); 304 Clang.takeDiagnosticClient(); 305 Clang.takeDiagnostics(); 306 return 0; 307} 308 309ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, 310 const char **ArgEnd, 311 Diagnostic &Diags, 312 llvm::StringRef ResourceFilesPath, 313 bool OnlyLocalDecls, 314 bool UseBumpAllocator, 315 RemappedFile *RemappedFiles, 316 unsigned NumRemappedFiles) { 317 llvm::SmallVector<const char *, 16> Args; 318 Args.push_back("<clang>"); // FIXME: Remove dummy argument. 319 Args.insert(Args.end(), ArgBegin, ArgEnd); 320 321 // FIXME: Find a cleaner way to force the driver into restricted modes. We 322 // also want to force it to use clang. 323 Args.push_back("-fsyntax-only"); 324 325 // FIXME: We shouldn't have to pass in the path info. 326 driver::Driver TheDriver("clang", "/", llvm::sys::getHostTriple(), 327 "a.out", false, Diags); 328 329 // Don't check that inputs exist, they have been remapped. 330 TheDriver.setCheckInputsExist(false); 331 332 llvm::OwningPtr<driver::Compilation> C( 333 TheDriver.BuildCompilation(Args.size(), Args.data())); 334 335 // We expect to get back exactly one command job, if we didn't something 336 // failed. 337 const driver::JobList &Jobs = C->getJobs(); 338 if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) { 339 llvm::SmallString<256> Msg; 340 llvm::raw_svector_ostream OS(Msg); 341 C->PrintJob(OS, C->getJobs(), "; ", true); 342 Diags.Report(diag::err_fe_expected_compiler_job) << OS.str(); 343 return 0; 344 } 345 346 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); 347 if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { 348 Diags.Report(diag::err_fe_expected_clang_command); 349 return 0; 350 } 351 352 const driver::ArgStringList &CCArgs = Cmd->getArguments(); 353 llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation); 354 CompilerInvocation::CreateFromArgs(*CI, (const char**) CCArgs.data(), 355 (const char**) CCArgs.data()+CCArgs.size(), 356 Diags); 357 358 // Override any files that need remapping 359 for (unsigned I = 0; I != NumRemappedFiles; ++I) 360 CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, 361 RemappedFiles[I].second); 362 363 // Override the resources path. 364 CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; 365 366 CI->getFrontendOpts().DisableFree = UseBumpAllocator; 367 ASTUnit *Unit = LoadFromCompilerInvocation(*CI, Diags, OnlyLocalDecls); 368 if (Unit) 369 Unit->Invocation.reset(CI.take()); 370 371 return Unit; 372} 373