ASTUnit.cpp revision cb6dda1a7abe2fe30b0345bfbbf872cc73446678
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/System/Host.h" 34#include "llvm/System/Path.h" 35using namespace clang; 36 37ASTUnit::ASTUnit(DiagnosticClient *diagClient) : tempFile(false) { 38 Diags.setClient(diagClient ? diagClient : new TextDiagnosticBuffer()); 39} 40ASTUnit::~ASTUnit() { 41 if (tempFile) 42 llvm::sys::Path(getPCHFileName()).eraseFromDisk(); 43 44 // The ASTUnit object owns the DiagnosticClient. 45 delete Diags.getClient(); 46} 47 48namespace { 49 50/// \brief Gathers information from PCHReader that will be used to initialize 51/// a Preprocessor. 52class PCHInfoCollector : public PCHReaderListener { 53 LangOptions &LangOpt; 54 HeaderSearch &HSI; 55 std::string &TargetTriple; 56 std::string &Predefines; 57 unsigned &Counter; 58 59 unsigned NumHeaderInfos; 60 61public: 62 PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI, 63 std::string &TargetTriple, std::string &Predefines, 64 unsigned &Counter) 65 : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple), 66 Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {} 67 68 virtual bool ReadLanguageOptions(const LangOptions &LangOpts) { 69 LangOpt = LangOpts; 70 return false; 71 } 72 73 virtual bool ReadTargetTriple(llvm::StringRef Triple) { 74 TargetTriple = Triple; 75 return false; 76 } 77 78 virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, 79 FileID PCHBufferID, 80 llvm::StringRef OriginalFileName, 81 std::string &SuggestedPredefines) { 82 Predefines = PCHPredef; 83 return false; 84 } 85 86 virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) { 87 HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++); 88 } 89 90 virtual void ReadCounter(unsigned Value) { 91 Counter = Value; 92 } 93}; 94 95} // anonymous namespace 96 97const std::string &ASTUnit::getOriginalSourceFileName() { 98 return dyn_cast<PCHReader>(Ctx->getExternalSource())->getOriginalSourceFile(); 99} 100 101const std::string &ASTUnit::getPCHFileName() { 102 return dyn_cast<PCHReader>(Ctx->getExternalSource())->getFileName(); 103} 104 105ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename, 106 std::string *ErrMsg, 107 DiagnosticClient *diagClient, 108 bool OnlyLocalDecls, 109 bool UseBumpAllocator) { 110 llvm::OwningPtr<ASTUnit> AST(new ASTUnit(diagClient)); 111 AST->OnlyLocalDecls = OnlyLocalDecls; 112 AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager())); 113 114 // Gather Info for preprocessor construction later on. 115 116 LangOptions LangInfo; 117 HeaderSearch &HeaderInfo = *AST->HeaderInfo.get(); 118 std::string TargetTriple; 119 std::string Predefines; 120 unsigned Counter; 121 122 llvm::OwningPtr<PCHReader> Reader; 123 llvm::OwningPtr<ExternalASTSource> Source; 124 125 Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(), 126 AST->Diags)); 127 Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple, 128 Predefines, Counter)); 129 130 switch (Reader->ReadPCH(Filename)) { 131 case PCHReader::Success: 132 break; 133 134 case PCHReader::Failure: 135 case PCHReader::IgnorePCH: 136 if (ErrMsg) 137 *ErrMsg = "Could not load PCH file"; 138 return NULL; 139 } 140 141 // PCH loaded successfully. Now create the preprocessor. 142 143 // Get information about the target being compiled for. 144 // 145 // FIXME: This is broken, we should store the TargetOptions in the PCH. 146 TargetOptions TargetOpts; 147 TargetOpts.ABI = ""; 148 TargetOpts.CPU = ""; 149 TargetOpts.Features.clear(); 150 TargetOpts.Triple = TargetTriple; 151 AST->Target.reset(TargetInfo::CreateTargetInfo(AST->Diags, TargetOpts)); 152 AST->PP.reset(new Preprocessor(AST->Diags, LangInfo, *AST->Target.get(), 153 AST->getSourceManager(), HeaderInfo)); 154 Preprocessor &PP = *AST->PP.get(); 155 156 PP.setPredefines(Reader->getSuggestedPredefines()); 157 PP.setCounterValue(Counter); 158 Reader->setPreprocessor(PP); 159 160 // Create and initialize the ASTContext. 161 162 AST->Ctx.reset(new ASTContext(LangInfo, 163 AST->getSourceManager(), 164 *AST->Target.get(), 165 PP.getIdentifierTable(), 166 PP.getSelectorTable(), 167 PP.getBuiltinInfo(), 168 /* FreeMemory = */ !UseBumpAllocator, 169 /* size_reserve = */0)); 170 ASTContext &Context = *AST->Ctx.get(); 171 172 Reader->InitializeContext(Context); 173 174 // Attach the PCH reader to the AST context as an external AST 175 // source, so that declarations will be deserialized from the 176 // PCH file as needed. 177 Source.reset(Reader.take()); 178 Context.setExternalSource(Source); 179 180 return AST.take(); 181} 182 183namespace { 184 185class NullAction : public ASTFrontendAction { 186 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 187 llvm::StringRef InFile) { 188 return new ASTConsumer(); 189 } 190 191public: 192 virtual bool hasCodeCompletionSupport() const { return false; } 193}; 194 195} 196 197ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI, 198 Diagnostic &Diags, 199 bool OnlyLocalDecls, 200 bool UseBumpAllocator) { 201 // Create the compiler instance to use for building the AST. 202 CompilerInstance Clang; 203 llvm::OwningPtr<ASTUnit> AST; 204 NullAction Act; 205 206 Clang.getInvocation() = CI; 207 208 Clang.setDiagnostics(&Diags); 209 Clang.setDiagnosticClient(Diags.getClient()); 210 211 // Create the target instance. 212 Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), 213 Clang.getTargetOpts())); 214 if (!Clang.hasTarget()) 215 goto error; 216 217 // Inform the target of the language options. 218 // 219 // FIXME: We shouldn't need to do this, the target should be immutable once 220 // created. This complexity should be lifted elsewhere. 221 Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); 222 223 assert(Clang.getFrontendOpts().Inputs.size() == 1 && 224 "Invocation must have exactly one source file!"); 225 assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST && 226 "FIXME: AST inputs not yet supported here!"); 227 228 // Create the AST unit. 229 // 230 // FIXME: Use the provided diagnostic client. 231 AST.reset(new ASTUnit()); 232 233 // Create a file manager object to provide access to and cache the filesystem. 234 Clang.setFileManager(&AST->getFileManager()); 235 236 // Create the source manager. 237 Clang.setSourceManager(&AST->getSourceManager()); 238 239 // Create the preprocessor. 240 Clang.createPreprocessor(); 241 242 if (!Act.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, 243 /*IsAST=*/false)) 244 goto error; 245 246 Act.Execute(); 247 248 // Steal the created target, context, and preprocessor, and take back the 249 // source and file managers. 250 AST->Ctx.reset(Clang.takeASTContext()); 251 AST->PP.reset(Clang.takePreprocessor()); 252 Clang.takeSourceManager(); 253 Clang.takeFileManager(); 254 AST->Target.reset(Clang.takeTarget()); 255 256 Act.EndSourceFile(); 257 258 Clang.takeDiagnosticClient(); 259 Clang.takeDiagnostics(); 260 261 return AST.take(); 262 263error: 264 Clang.takeSourceManager(); 265 Clang.takeFileManager(); 266 Clang.takeDiagnosticClient(); 267 Clang.takeDiagnostics(); 268 return 0; 269} 270 271ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, 272 const char **ArgEnd, 273 Diagnostic &Diags, 274 const char *Argv0, 275 void *MainAddr, 276 bool OnlyLocalDecls, 277 bool UseBumpAllocator) { 278 llvm::SmallVector<const char *, 16> Args; 279 Args.push_back("<clang>"); // FIXME: Remove dummy argument. 280 Args.insert(Args.end(), ArgBegin, ArgEnd); 281 282 // FIXME: Find a cleaner way to force the driver into restricted modes. We 283 // also want to force it to use clang. 284 Args.push_back("-fsyntax-only"); 285 286 llvm::sys::Path Path = llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); 287 driver::Driver TheDriver(Path.getBasename().c_str(),Path.getDirname().c_str(), 288 llvm::sys::getHostTriple().c_str(), 289 "a.out", false, Diags); 290 llvm::OwningPtr<driver::Compilation> C( 291 TheDriver.BuildCompilation(Args.size(), Args.data())); 292 293 // We expect to get back exactly one command job, if we didn't something 294 // failed. 295 const driver::JobList &Jobs = C->getJobs(); 296 if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) { 297 llvm::SmallString<256> Msg; 298 llvm::raw_svector_ostream OS(Msg); 299 C->PrintJob(OS, C->getJobs(), "; ", true); 300 Diags.Report(diag::err_fe_expected_compiler_job) << OS.str(); 301 return 0; 302 } 303 304 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); 305 if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { 306 Diags.Report(diag::err_fe_expected_clang_command); 307 return 0; 308 } 309 310 const driver::ArgStringList &CCArgs = Cmd->getArguments(); 311 CompilerInvocation CI; 312 CompilerInvocation::CreateFromArgs(CI, (const char**) CCArgs.data(), 313 (const char**) CCArgs.data()+CCArgs.size(), 314 Argv0, MainAddr, Diags); 315 316 return LoadFromCompilerInvocation(CI, Diags, OnlyLocalDecls, 317 UseBumpAllocator); 318} 319