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