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