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