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" 36 using namespace clang; 37 38 ASTUnit::ASTUnit(bool _MainFileIsAST) 39 : MainFileIsAST(_MainFileIsAST), ConcurrencyCheckValue(CheckUnlocked) { } 40 41 ASTUnit::~ASTUnit() { 42 ConcurrencyCheckValue = CheckLocked; 43 for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) 44 TemporaryFiles[I].eraseFromDisk(); 45 } 46 47 namespace { 48 49 /// \brief Gathers information from PCHReader that will be used to initialize 50 /// a Preprocessor. 51 class 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 60 public: 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(const PCHPredefinesBlocks &Buffers, 78 llvm::StringRef OriginalFileName, 79 std::string &SuggestedPredefines) { 80 Predefines = Buffers[0].Data; 81 for (unsigned I = 1, N = Buffers.size(); I != N; ++I) { 82 Predefines += Buffers[I].Data; 83 } 84 return false; 85 } 86 87 virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID) { 88 HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++); 89 } 90 91 virtual void ReadCounter(unsigned Value) { 92 Counter = Value; 93 } 94 }; 95 96 class StoredDiagnosticClient : public DiagnosticClient { 97 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags; 98 99 public: 100 explicit StoredDiagnosticClient( 101 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) 102 : StoredDiags(StoredDiags) { } 103 104 virtual void HandleDiagnostic(Diagnostic::Level Level, 105 const DiagnosticInfo &Info); 106 }; 107 108 /// \brief RAII object that optionally captures diagnostics, if 109 /// there is no diagnostic client to capture them already. 110 class CaptureDroppedDiagnostics { 111 Diagnostic &Diags; 112 StoredDiagnosticClient Client; 113 DiagnosticClient *PreviousClient; 114 115 public: 116 CaptureDroppedDiagnostics(bool RequestCapture, Diagnostic &Diags, 117 llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) 118 : Diags(Diags), Client(StoredDiags), PreviousClient(Diags.getClient()) 119 { 120 if (RequestCapture || Diags.getClient() == 0) 121 Diags.setClient(&Client); 122 } 123 124 ~CaptureDroppedDiagnostics() { 125 Diags.setClient(PreviousClient); 126 } 127 }; 128 129 } // anonymous namespace 130 131 void StoredDiagnosticClient::HandleDiagnostic(Diagnostic::Level Level, 132 const DiagnosticInfo &Info) { 133 StoredDiags.push_back(StoredDiagnostic(Level, Info)); 134 } 135 136 const std::string &ASTUnit::getOriginalSourceFileName() { 137 return OriginalSourceFile; 138 } 139 140 const std::string &ASTUnit::getPCHFileName() { 141 assert(isMainFileAST() && "Not an ASTUnit from a PCH file!"); 142 return static_cast<PCHReader *>(Ctx->getExternalSource())->getFileName(); 143 } 144 145 ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename, 146 llvm::IntrusiveRefCntPtr<Diagnostic> Diags, 147 bool OnlyLocalDecls, 148 RemappedFile *RemappedFiles, 149 unsigned NumRemappedFiles, 150 bool CaptureDiagnostics) { 151 llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true)); 152 153 if (!Diags.getPtr()) { 154 // No diagnostics engine was provided, so create our own diagnostics object 155 // with the default options. 156 DiagnosticOptions DiagOpts; 157 Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); 158 } 159 160 AST->OnlyLocalDecls = OnlyLocalDecls; 161 AST->Diagnostics = Diags; 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.CXXABI = "itanium"; 225 TargetOpts.CPU = ""; 226 TargetOpts.Features.clear(); 227 TargetOpts.Triple = TargetTriple; 228 AST->Target.reset(TargetInfo::CreateTargetInfo(AST->getDiagnostics(), 229 TargetOpts)); 230 AST->PP.reset(new Preprocessor(AST->getDiagnostics(), LangInfo, 231 *AST->Target.get(), 232 AST->getSourceManager(), HeaderInfo)); 233 Preprocessor &PP = *AST->PP.get(); 234 235 PP.setPredefines(Reader->getSuggestedPredefines()); 236 PP.setCounterValue(Counter); 237 Reader->setPreprocessor(PP); 238 239 // Create and initialize the ASTContext. 240 241 AST->Ctx.reset(new ASTContext(LangInfo, 242 AST->getSourceManager(), 243 *AST->Target.get(), 244 PP.getIdentifierTable(), 245 PP.getSelectorTable(), 246 PP.getBuiltinInfo(), 247 /* FreeMemory = */ false, 248 /* size_reserve = */0)); 249 ASTContext &Context = *AST->Ctx.get(); 250 251 Reader->InitializeContext(Context); 252 253 // Attach the PCH reader to the AST context as an external AST 254 // source, so that declarations will be deserialized from the 255 // PCH file as needed. 256 Source.reset(Reader.take()); 257 Context.setExternalSource(Source); 258 259 return AST.take(); 260 } 261 262 namespace { 263 264 class TopLevelDeclTrackerConsumer : public ASTConsumer { 265 ASTUnit &Unit; 266 267 public: 268 TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {} 269 270 void HandleTopLevelDecl(DeclGroupRef D) { 271 for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { 272 Decl *D = *it; 273 // FIXME: Currently ObjC method declarations are incorrectly being 274 // reported as top-level declarations, even though their DeclContext 275 // is the containing ObjC @interface/@implementation. This is a 276 // fundamental problem in the parser right now. 277 if (isa<ObjCMethodDecl>(D)) 278 continue; 279 Unit.getTopLevelDecls().push_back(D); 280 } 281 } 282 }; 283 284 class TopLevelDeclTrackerAction : public ASTFrontendAction { 285 public: 286 ASTUnit &Unit; 287 288 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, 289 llvm::StringRef InFile) { 290 return new TopLevelDeclTrackerConsumer(Unit); 291 } 292 293 public: 294 TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} 295 296 virtual bool hasCodeCompletionSupport() const { return false; } 297 }; 298 299 } 300 301 ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, 302 llvm::IntrusiveRefCntPtr<Diagnostic> Diags, 303 bool OnlyLocalDecls, 304 bool CaptureDiagnostics) { 305 // Create the compiler instance to use for building the AST. 306 CompilerInstance Clang; 307 llvm::OwningPtr<ASTUnit> AST; 308 llvm::OwningPtr<TopLevelDeclTrackerAction> Act; 309 310 if (!Diags.getPtr()) { 311 // No diagnostics engine was provided, so create our own diagnostics object 312 // with the default options. 313 DiagnosticOptions DiagOpts; 314 Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); 315 } 316 317 Clang.setInvocation(CI); 318 319 Clang.setDiagnostics(Diags.getPtr()); 320 Clang.setDiagnosticClient(Diags->getClient()); 321 322 // Create the target instance. 323 Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), 324 Clang.getTargetOpts())); 325 if (!Clang.hasTarget()) { 326 Clang.takeDiagnosticClient(); 327 return 0; 328 } 329 330 // Inform the target of the language options. 331 // 332 // FIXME: We shouldn't need to do this, the target should be immutable once 333 // created. This complexity should be lifted elsewhere. 334 Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); 335 336 assert(Clang.getFrontendOpts().Inputs.size() == 1 && 337 "Invocation must have exactly one source file!"); 338 assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST && 339 "FIXME: AST inputs not yet supported here!"); 340 assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR && 341 "IR inputs not support here!"); 342 343 // Create the AST unit. 344 AST.reset(new ASTUnit(false)); 345 AST->Diagnostics = Diags; 346 AST->FileMgr.reset(new FileManager); 347 AST->SourceMgr.reset(new SourceManager(AST->getDiagnostics())); 348 AST->OnlyLocalDecls = OnlyLocalDecls; 349 AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; 350 351 // Capture any diagnostics that would otherwise be dropped. 352 CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 353 Clang.getDiagnostics(), 354 AST->StoredDiagnostics); 355 356 // Create a file manager object to provide access to and cache the filesystem. 357 Clang.setFileManager(&AST->getFileManager()); 358 359 // Create the source manager. 360 Clang.setSourceManager(&AST->getSourceManager()); 361 362 Act.reset(new TopLevelDeclTrackerAction(*AST)); 363 if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, 364 Clang.getFrontendOpts().Inputs[0].first)) 365 goto error; 366 367 Act->Execute(); 368 369 // Steal the created target, context, and preprocessor, and take back the 370 // source and file managers. 371 AST->Ctx.reset(Clang.takeASTContext()); 372 AST->PP.reset(Clang.takePreprocessor()); 373 Clang.takeSourceManager(); 374 Clang.takeFileManager(); 375 AST->Target.reset(Clang.takeTarget()); 376 377 Act->EndSourceFile(); 378 379 Clang.takeDiagnosticClient(); 380 Clang.takeInvocation(); 381 382 AST->Invocation.reset(Clang.takeInvocation()); 383 return AST.take(); 384 385 error: 386 Clang.takeSourceManager(); 387 Clang.takeFileManager(); 388 Clang.takeDiagnosticClient(); 389 return 0; 390 } 391 392 ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, 393 const char **ArgEnd, 394 llvm::IntrusiveRefCntPtr<Diagnostic> Diags, 395 llvm::StringRef ResourceFilesPath, 396 bool OnlyLocalDecls, 397 RemappedFile *RemappedFiles, 398 unsigned NumRemappedFiles, 399 bool CaptureDiagnostics) { 400 if (!Diags.getPtr()) { 401 // No diagnostics engine was provided, so create our own diagnostics object 402 // with the default options. 403 DiagnosticOptions DiagOpts; 404 Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); 405 } 406 407 llvm::SmallVector<const char *, 16> Args; 408 Args.push_back("<clang>"); // FIXME: Remove dummy argument. 409 Args.insert(Args.end(), ArgBegin, ArgEnd); 410 411 // FIXME: Find a cleaner way to force the driver into restricted modes. We 412 // also want to force it to use clang. 413 Args.push_back("-fsyntax-only"); 414 415 // FIXME: We shouldn't have to pass in the path info. 416 driver::Driver TheDriver("clang", "/", llvm::sys::getHostTriple(), 417 "a.out", false, false, *Diags); 418 419 // Don't check that inputs exist, they have been remapped. 420 TheDriver.setCheckInputsExist(false); 421 422 llvm::OwningPtr<driver::Compilation> C( 423 TheDriver.BuildCompilation(Args.size(), Args.data())); 424 425 // We expect to get back exactly one command job, if we didn't something 426 // failed. 427 const driver::JobList &Jobs = C->getJobs(); 428 if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) { 429 llvm::SmallString<256> Msg; 430 llvm::raw_svector_ostream OS(Msg); 431 C->PrintJob(OS, C->getJobs(), "; ", true); 432 Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); 433 return 0; 434 } 435 436 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); 437 if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { 438 Diags->Report(diag::err_fe_expected_clang_command); 439 return 0; 440 } 441 442 const driver::ArgStringList &CCArgs = Cmd->getArguments(); 443 llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation); 444 CompilerInvocation::CreateFromArgs(*CI, 445 const_cast<const char **>(CCArgs.data()), 446 const_cast<const char **>(CCArgs.data()) + 447 CCArgs.size(), 448 *Diags); 449 450 // Override any files that need remapping 451 for (unsigned I = 0; I != NumRemappedFiles; ++I) 452 CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, 453 RemappedFiles[I].second); 454 455 // Override the resources path. 456 CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; 457 458 CI->getFrontendOpts().DisableFree = true; 459 return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls, 460 CaptureDiagnostics); 461 } 462