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