ASTUnit.cpp revision 0f3039072ad62586586cbef5a24faee2f1bcfd1c
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 : MainFileIsAST(_MainFileIsAST), ConcurrencyCheckValue(CheckUnlocked) { } 40 41ASTUnit::~ASTUnit() { 42 ConcurrencyCheckValue = CheckLocked; 43 for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) 44 TemporaryFiles[I].eraseFromDisk(); 45} 46 47namespace { 48 49/// \brief Gathers information from PCHReader that will be used to initialize 50/// a Preprocessor. 51class PCHInfoCollector : public PCHReaderListener { 52 LangOptions &LangOpt; 53 HeaderSearch &HSI; 54 std::string &TargetTriple; 55 std::string &Predefines; 56 unsigned &Counter; 57 58 unsigned NumHeaderInfos; 59 60public: 61 PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI, 62 std::string &TargetTriple, std::string &Predefines, 63 unsigned &Counter) 64 : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple), 65 Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {} 66 67 virtual bool ReadLanguageOptions(const LangOptions &LangOpts) { 68 LangOpt = LangOpts; 69 return false; 70 } 71 72 virtual bool ReadTargetTriple(llvm::StringRef Triple) { 73 TargetTriple = Triple; 74 return false; 75 } 76 77 virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, 78 FileID PCHBufferID, 79 llvm::StringRef OriginalFileName, 80 std::string &SuggestedPredefines) { 81 Predefines = PCHPredef; 82 return false; 83 } 84 85 virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID) { 86 HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++); 87 } 88 89 virtual void ReadCounter(unsigned Value) { 90 Counter = Value; 91 } 92}; 93 94class StoredDiagnosticClient : public DiagnosticClient { 95 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags; 96 97public: 98 explicit StoredDiagnosticClient( 99 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) 100 : StoredDiags(StoredDiags) { } 101 102 virtual void HandleDiagnostic(Diagnostic::Level Level, 103 const DiagnosticInfo &Info); 104}; 105 106/// \brief RAII object that optionally captures diagnostics, if 107/// there is no diagnostic client to capture them already. 108class CaptureDroppedDiagnostics { 109 Diagnostic &Diags; 110 StoredDiagnosticClient Client; 111 DiagnosticClient *PreviousClient; 112 113public: 114 CaptureDroppedDiagnostics(bool RequestCapture, Diagnostic &Diags, 115 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) 116 : Diags(Diags), Client(StoredDiags), PreviousClient(Diags.getClient()) 117 { 118 if (RequestCapture || Diags.getClient() == 0) 119 Diags.setClient(&Client); 120 } 121 122 ~CaptureDroppedDiagnostics() { 123 Diags.setClient(PreviousClient); 124 } 125}; 126 127} // anonymous namespace 128 129void StoredDiagnosticClient::HandleDiagnostic(Diagnostic::Level Level, 130 const DiagnosticInfo &Info) { 131 StoredDiags.push_back(StoredDiagnostic(Level, Info)); 132} 133 134const std::string &ASTUnit::getOriginalSourceFileName() { 135 return OriginalSourceFile; 136} 137 138const std::string &ASTUnit::getPCHFileName() { 139 assert(isMainFileAST() && "Not an ASTUnit from a PCH file!"); 140 return static_cast<PCHReader *>(Ctx->getExternalSource())->getFileName(); 141} 142 143ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename, 144 llvm::MaybeOwningPtr<Diagnostic> Diags, 145 bool OnlyLocalDecls, 146 RemappedFile *RemappedFiles, 147 unsigned NumRemappedFiles, 148 bool CaptureDiagnostics) { 149 llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true)); 150 151 if (Diags.get()) 152 AST->Diagnostics = Diags; 153 else { 154 // No diagnostics engine was provided, so create our own diagnostics object 155 // with the default options. 156 DiagnosticOptions DiagOpts; 157 AST->Diagnostics.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0), 158 true); 159 } 160 161 AST->OnlyLocalDecls = OnlyLocalDecls; 162 AST->FileMgr.reset(new FileManager); 163 AST->SourceMgr.reset(new SourceManager(AST->getDiagnostics())); 164 AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager())); 165 166 // If requested, capture diagnostics in the ASTUnit. 167 CaptureDroppedDiagnostics Capture(CaptureDiagnostics, AST->getDiagnostics(), 168 AST->StoredDiagnostics); 169 170 for (unsigned I = 0; I != NumRemappedFiles; ++I) { 171 // Create the file entry for the file that we're mapping from. 172 const FileEntry *FromFile 173 = AST->getFileManager().getVirtualFile(RemappedFiles[I].first, 174 RemappedFiles[I].second->getBufferSize(), 175 0); 176 if (!FromFile) { 177 AST->getDiagnostics().Report(diag::err_fe_remap_missing_from_file) 178 << RemappedFiles[I].first; 179 delete RemappedFiles[I].second; 180 continue; 181 } 182 183 // Override the contents of the "from" file with the contents of 184 // the "to" file. 185 AST->getSourceManager().overrideFileContents(FromFile, 186 RemappedFiles[I].second); 187 } 188 189 // Gather Info for preprocessor construction later on. 190 191 LangOptions LangInfo; 192 HeaderSearch &HeaderInfo = *AST->HeaderInfo.get(); 193 std::string TargetTriple; 194 std::string Predefines; 195 unsigned Counter; 196 197 llvm::OwningPtr<PCHReader> Reader; 198 llvm::OwningPtr<ExternalASTSource> Source; 199 200 Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(), 201 AST->getDiagnostics())); 202 Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple, 203 Predefines, Counter)); 204 205 switch (Reader->ReadPCH(Filename)) { 206 case PCHReader::Success: 207 break; 208 209 case PCHReader::Failure: 210 case PCHReader::IgnorePCH: 211 AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch); 212 return NULL; 213 } 214 215 AST->OriginalSourceFile = Reader->getOriginalSourceFile(); 216 217 // PCH loaded successfully. Now create the preprocessor. 218 219 // Get information about the target being compiled for. 220 // 221 // FIXME: This is broken, we should store the TargetOptions in the PCH. 222 TargetOptions TargetOpts; 223 TargetOpts.ABI = ""; 224 TargetOpts.CPU = ""; 225 TargetOpts.Features.clear(); 226 TargetOpts.Triple = TargetTriple; 227 AST->Target.reset(TargetInfo::CreateTargetInfo(AST->getDiagnostics(), 228 TargetOpts)); 229 AST->PP.reset(new Preprocessor(AST->getDiagnostics(), LangInfo, 230 *AST->Target.get(), 231 AST->getSourceManager(), HeaderInfo)); 232 Preprocessor &PP = *AST->PP.get(); 233 234 PP.setPredefines(Reader->getSuggestedPredefines()); 235 PP.setCounterValue(Counter); 236 Reader->setPreprocessor(PP); 237 238 // Create and initialize the ASTContext. 239 240 AST->Ctx.reset(new ASTContext(LangInfo, 241 AST->getSourceManager(), 242 *AST->Target.get(), 243 PP.getIdentifierTable(), 244 PP.getSelectorTable(), 245 PP.getBuiltinInfo(), 246 /* FreeMemory = */ false, 247 /* size_reserve = */0)); 248 ASTContext &Context = *AST->Ctx.get(); 249 250 Reader->InitializeContext(Context); 251 252 // Attach the PCH reader to the AST context as an external AST 253 // source, so that declarations will be deserialized from the 254 // PCH file as needed. 255 Source.reset(Reader.take()); 256 Context.setExternalSource(Source); 257 258 return AST.take(); 259} 260 261namespace { 262 263class TopLevelDeclTrackerConsumer : public ASTConsumer { 264 ASTUnit &Unit; 265 266public: 267 TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {} 268 269 void HandleTopLevelDecl(DeclGroupRef D) { 270 for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) 271 Unit.getTopLevelDecls().push_back(*it); 272 } 273}; 274 275class TopLevelDeclTrackerAction : public ASTFrontendAction { 276public: 277 ASTUnit &Unit; 278 279 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 280 llvm::StringRef InFile) { 281 return new TopLevelDeclTrackerConsumer(Unit); 282 } 283 284public: 285 TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} 286 287 virtual bool hasCodeCompletionSupport() const { return false; } 288}; 289 290} 291 292ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, 293 llvm::MaybeOwningPtr<Diagnostic> Diags, 294 bool OnlyLocalDecls, 295 bool CaptureDiagnostics) { 296 // Create the compiler instance to use for building the AST. 297 CompilerInstance Clang; 298 llvm::OwningPtr<ASTUnit> AST; 299 llvm::OwningPtr<TopLevelDeclTrackerAction> Act; 300 301 if (!Diags.get()) { 302 // No diagnostics engine was provided, so create our own diagnostics object 303 // with the default options. 304 DiagnosticOptions DiagOpts; 305 Diags.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0), true); 306 } 307 308 Clang.setInvocation(CI); 309 310 Clang.setDiagnostics(Diags.get()); 311 Clang.setDiagnosticClient(Diags->getClient()); 312 313 // Create the target instance. 314 Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), 315 Clang.getTargetOpts())); 316 if (!Clang.hasTarget()) { 317 Clang.takeDiagnosticClient(); 318 Clang.takeDiagnostics(); 319 return 0; 320 } 321 322 // Inform the target of the language options. 323 // 324 // FIXME: We shouldn't need to do this, the target should be immutable once 325 // created. This complexity should be lifted elsewhere. 326 Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); 327 328 assert(Clang.getFrontendOpts().Inputs.size() == 1 && 329 "Invocation must have exactly one source file!"); 330 assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST && 331 "FIXME: AST inputs not yet supported here!"); 332 333 // Create the AST unit. 334 AST.reset(new ASTUnit(false)); 335 AST->Diagnostics = Diags; 336 AST->FileMgr.reset(new FileManager); 337 AST->SourceMgr.reset(new SourceManager(AST->getDiagnostics())); 338 AST->OnlyLocalDecls = OnlyLocalDecls; 339 AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; 340 341 // Capture any diagnostics that would otherwise be dropped. 342 CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 343 Clang.getDiagnostics(), 344 AST->StoredDiagnostics); 345 346 // Create a file manager object to provide access to and cache the filesystem. 347 Clang.setFileManager(&AST->getFileManager()); 348 349 // Create the source manager. 350 Clang.setSourceManager(&AST->getSourceManager()); 351 352 // Create the preprocessor. 353 Clang.createPreprocessor(); 354 355 Act.reset(new TopLevelDeclTrackerAction(*AST)); 356 if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, 357 /*IsAST=*/false)) 358 goto error; 359 360 Act->Execute(); 361 362 // Steal the created target, context, and preprocessor, and take back the 363 // source and file managers. 364 AST->Ctx.reset(Clang.takeASTContext()); 365 AST->PP.reset(Clang.takePreprocessor()); 366 Clang.takeSourceManager(); 367 Clang.takeFileManager(); 368 AST->Target.reset(Clang.takeTarget()); 369 370 Act->EndSourceFile(); 371 372 Clang.takeDiagnosticClient(); 373 Clang.takeDiagnostics(); 374 Clang.takeInvocation(); 375 376 AST->Invocation.reset(Clang.takeInvocation()); 377 return AST.take(); 378 379error: 380 Clang.takeSourceManager(); 381 Clang.takeFileManager(); 382 Clang.takeDiagnosticClient(); 383 Clang.takeDiagnostics(); 384 return 0; 385} 386 387ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, 388 const char **ArgEnd, 389 llvm::MaybeOwningPtr<Diagnostic> Diags, 390 llvm::StringRef ResourceFilesPath, 391 bool OnlyLocalDecls, 392 RemappedFile *RemappedFiles, 393 unsigned NumRemappedFiles, 394 bool CaptureDiagnostics) { 395 if (!Diags.get()) { 396 // No diagnostics engine was provided, so create our own diagnostics object 397 // with the default options. 398 DiagnosticOptions DiagOpts; 399 Diags.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0), true); 400 } 401 402 llvm::SmallVector<const char *, 16> Args; 403 Args.push_back("<clang>"); // FIXME: Remove dummy argument. 404 Args.insert(Args.end(), ArgBegin, ArgEnd); 405 406 // FIXME: Find a cleaner way to force the driver into restricted modes. We 407 // also want to force it to use clang. 408 Args.push_back("-fsyntax-only"); 409 410 // FIXME: We shouldn't have to pass in the path info. 411 driver::Driver TheDriver("clang", "/", llvm::sys::getHostTriple(), 412 "a.out", false, false, *Diags); 413 414 // Don't check that inputs exist, they have been remapped. 415 TheDriver.setCheckInputsExist(false); 416 417 llvm::OwningPtr<driver::Compilation> C( 418 TheDriver.BuildCompilation(Args.size(), Args.data())); 419 420 // We expect to get back exactly one command job, if we didn't something 421 // failed. 422 const driver::JobList &Jobs = C->getJobs(); 423 if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) { 424 llvm::SmallString<256> Msg; 425 llvm::raw_svector_ostream OS(Msg); 426 C->PrintJob(OS, C->getJobs(), "; ", true); 427 Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); 428 return 0; 429 } 430 431 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); 432 if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { 433 Diags->Report(diag::err_fe_expected_clang_command); 434 return 0; 435 } 436 437 const driver::ArgStringList &CCArgs = Cmd->getArguments(); 438 llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation); 439 CompilerInvocation::CreateFromArgs(*CI, (const char**) CCArgs.data(), 440 (const char**) CCArgs.data()+CCArgs.size(), 441 *Diags); 442 443 // Override any files that need remapping 444 for (unsigned I = 0; I != NumRemappedFiles; ++I) 445 CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, 446 RemappedFiles[I].second); 447 448 // Override the resources path. 449 CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; 450 451 CI->getFrontendOpts().DisableFree = true; 452 return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls, 453 CaptureDiagnostics); 454} 455