ASTUnit.cpp revision c7822dbf3c01a2a5f837cff82ba7889ea755daca
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/System/Host.h"
34#include "llvm/System/Path.h"
35using namespace clang;
36
37ASTUnit::ASTUnit(bool _MainFileIsAST,
38                 DiagnosticClient *diagClient)
39  : tempFile(false), MainFileIsAST(_MainFileIsAST)
40{
41  Diags.setClient(diagClient ? diagClient : new TextDiagnosticBuffer());
42}
43ASTUnit::~ASTUnit() {
44  if (tempFile)
45    llvm::sys::Path(getPCHFileName()).eraseFromDisk();
46
47  //  The ASTUnit object owns the DiagnosticClient.
48  delete Diags.getClient();
49}
50
51namespace {
52
53/// \brief Gathers information from PCHReader that will be used to initialize
54/// a Preprocessor.
55class PCHInfoCollector : public PCHReaderListener {
56  LangOptions &LangOpt;
57  HeaderSearch &HSI;
58  std::string &TargetTriple;
59  std::string &Predefines;
60  unsigned &Counter;
61
62  unsigned NumHeaderInfos;
63
64public:
65  PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI,
66                   std::string &TargetTriple, std::string &Predefines,
67                   unsigned &Counter)
68    : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple),
69      Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {}
70
71  virtual bool ReadLanguageOptions(const LangOptions &LangOpts) {
72    LangOpt = LangOpts;
73    return false;
74  }
75
76  virtual bool ReadTargetTriple(llvm::StringRef Triple) {
77    TargetTriple = Triple;
78    return false;
79  }
80
81  virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef,
82                                    FileID PCHBufferID,
83                                    llvm::StringRef OriginalFileName,
84                                    std::string &SuggestedPredefines) {
85    Predefines = PCHPredef;
86    return false;
87  }
88
89  virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) {
90    HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++);
91  }
92
93  virtual void ReadCounter(unsigned Value) {
94    Counter = Value;
95  }
96};
97
98} // anonymous namespace
99
100const std::string &ASTUnit::getOriginalSourceFileName() {
101  return OriginalSourceFile;
102}
103
104const std::string &ASTUnit::getPCHFileName() {
105  assert(isMainFileAST() && "Not an ASTUnit from a PCH file!");
106  return dyn_cast<PCHReader>(Ctx->getExternalSource())->getFileName();
107}
108
109ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename,
110                                  std::string *ErrMsg,
111                                  DiagnosticClient *diagClient,
112                                  bool OnlyLocalDecls,
113                                  bool UseBumpAllocator) {
114  llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true, diagClient));
115  AST->OnlyLocalDecls = OnlyLocalDecls;
116  AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager()));
117
118  // Gather Info for preprocessor construction later on.
119
120  LangOptions LangInfo;
121  HeaderSearch &HeaderInfo = *AST->HeaderInfo.get();
122  std::string TargetTriple;
123  std::string Predefines;
124  unsigned Counter;
125
126  llvm::OwningPtr<PCHReader> Reader;
127  llvm::OwningPtr<ExternalASTSource> Source;
128
129  Reader.reset(new PCHReader(AST->getSourceManager(), AST->getFileManager(),
130                             AST->Diags));
131  Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple,
132                                           Predefines, Counter));
133
134  switch (Reader->ReadPCH(Filename)) {
135  case PCHReader::Success:
136    break;
137
138  case PCHReader::Failure:
139  case PCHReader::IgnorePCH:
140    if (ErrMsg)
141      *ErrMsg = "Could not load PCH file";
142    return NULL;
143  }
144
145  AST->OriginalSourceFile = Reader->getOriginalSourceFile();
146
147  // PCH loaded successfully. Now create the preprocessor.
148
149  // Get information about the target being compiled for.
150  //
151  // FIXME: This is broken, we should store the TargetOptions in the PCH.
152  TargetOptions TargetOpts;
153  TargetOpts.ABI = "";
154  TargetOpts.CPU = "";
155  TargetOpts.Features.clear();
156  TargetOpts.Triple = TargetTriple;
157  AST->Target.reset(TargetInfo::CreateTargetInfo(AST->Diags, TargetOpts));
158  AST->PP.reset(new Preprocessor(AST->Diags, LangInfo, *AST->Target.get(),
159                                 AST->getSourceManager(), HeaderInfo));
160  Preprocessor &PP = *AST->PP.get();
161
162  PP.setPredefines(Reader->getSuggestedPredefines());
163  PP.setCounterValue(Counter);
164  Reader->setPreprocessor(PP);
165
166  // Create and initialize the ASTContext.
167
168  AST->Ctx.reset(new ASTContext(LangInfo,
169                                AST->getSourceManager(),
170                                *AST->Target.get(),
171                                PP.getIdentifierTable(),
172                                PP.getSelectorTable(),
173                                PP.getBuiltinInfo(),
174                                /* FreeMemory = */ !UseBumpAllocator,
175                                /* size_reserve = */0));
176  ASTContext &Context = *AST->Ctx.get();
177
178  Reader->InitializeContext(Context);
179
180  // Attach the PCH reader to the AST context as an external AST
181  // source, so that declarations will be deserialized from the
182  // PCH file as needed.
183  Source.reset(Reader.take());
184  Context.setExternalSource(Source);
185
186  return AST.take();
187}
188
189namespace {
190
191class NullAction : public ASTFrontendAction {
192  virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
193                                         llvm::StringRef InFile) {
194    return new ASTConsumer();
195  }
196
197public:
198  virtual bool hasCodeCompletionSupport() const { return false; }
199};
200
201}
202
203ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI,
204                                             Diagnostic &Diags,
205                                             bool OnlyLocalDecls) {
206  // Create the compiler instance to use for building the AST.
207  CompilerInstance Clang;
208  llvm::OwningPtr<ASTUnit> AST;
209  NullAction Act;
210
211  Clang.getInvocation() = CI;
212
213  Clang.setDiagnostics(&Diags);
214  Clang.setDiagnosticClient(Diags.getClient());
215
216  // Create the target instance.
217  Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
218                                               Clang.getTargetOpts()));
219  if (!Clang.hasTarget())
220    goto error;
221
222  // Inform the target of the language options.
223  //
224  // FIXME: We shouldn't need to do this, the target should be immutable once
225  // created. This complexity should be lifted elsewhere.
226  Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
227
228  assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
229         "Invocation must have exactly one source file!");
230  assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST &&
231         "FIXME: AST inputs not yet supported here!");
232
233  // Create the AST unit.
234  //
235  // FIXME: Use the provided diagnostic client.
236  AST.reset(new ASTUnit(false));
237
238  AST->OnlyLocalDecls = OnlyLocalDecls;
239  AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
240
241  // Create a file manager object to provide access to and cache the filesystem.
242  Clang.setFileManager(&AST->getFileManager());
243
244  // Create the source manager.
245  Clang.setSourceManager(&AST->getSourceManager());
246
247  // Create the preprocessor.
248  Clang.createPreprocessor();
249
250  if (!Act.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
251                           /*IsAST=*/false))
252    goto error;
253
254  Act.Execute();
255
256  // Steal the created target, context, and preprocessor, and take back the
257  // source and file managers.
258  AST->Ctx.reset(Clang.takeASTContext());
259  AST->PP.reset(Clang.takePreprocessor());
260  Clang.takeSourceManager();
261  Clang.takeFileManager();
262  AST->Target.reset(Clang.takeTarget());
263
264  Act.EndSourceFile();
265
266  Clang.takeDiagnosticClient();
267  Clang.takeDiagnostics();
268
269  return AST.take();
270
271error:
272  Clang.takeSourceManager();
273  Clang.takeFileManager();
274  Clang.takeDiagnosticClient();
275  Clang.takeDiagnostics();
276  return 0;
277}
278
279ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
280                                      const char **ArgEnd,
281                                      Diagnostic &Diags,
282                                      const char *Argv0,
283                                      void *MainAddr,
284                                      bool OnlyLocalDecls,
285                                      bool UseBumpAllocator) {
286  llvm::SmallVector<const char *, 16> Args;
287  Args.push_back("<clang>"); // FIXME: Remove dummy argument.
288  Args.insert(Args.end(), ArgBegin, ArgEnd);
289
290  // FIXME: Find a cleaner way to force the driver into restricted modes. We
291  // also want to force it to use clang.
292  Args.push_back("-fsyntax-only");
293
294  llvm::sys::Path Path = llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
295  driver::Driver TheDriver(Path.getBasename().c_str(),Path.getDirname().c_str(),
296                           llvm::sys::getHostTriple().c_str(),
297                           "a.out", false, Diags);
298  llvm::OwningPtr<driver::Compilation> C(
299    TheDriver.BuildCompilation(Args.size(), Args.data()));
300
301  // We expect to get back exactly one command job, if we didn't something
302  // failed.
303  const driver::JobList &Jobs = C->getJobs();
304  if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) {
305    llvm::SmallString<256> Msg;
306    llvm::raw_svector_ostream OS(Msg);
307    C->PrintJob(OS, C->getJobs(), "; ", true);
308    Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
309    return 0;
310  }
311
312  const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
313  if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
314    Diags.Report(diag::err_fe_expected_clang_command);
315    return 0;
316  }
317
318  const driver::ArgStringList &CCArgs = Cmd->getArguments();
319  CompilerInvocation CI;
320  CompilerInvocation::CreateFromArgs(CI, (const char**) CCArgs.data(),
321                                     (const char**) CCArgs.data()+CCArgs.size(),
322                                     Argv0, MainAddr, Diags);
323
324  CI.getFrontendOpts().DisableFree = UseBumpAllocator;
325  return LoadFromCompilerInvocation(CI, Diags, OnlyLocalDecls);
326}
327