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