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