1//===--- CreateASTUnitFromArgs.h - Create an ASTUnit from Args ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Utility for creating an ASTUnit from a vector of command line arguments.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Driver/CreateASTUnitFromArgs.h"
14#include "clang/Driver/CreateInvocationFromArgs.h"
15#include "clang/Frontend/CompilerInvocation.h"
16#include "clang/Lex/PreprocessorOptions.h"
17#include "clang/Serialization/ModuleCache.h"
18#include "llvm/Support/CrashRecoveryContext.h"
19
20using namespace clang;
21
22/// Create an ASTUnit from a vector of command line arguments, which must
23/// specify exactly one source file.
24///
25/// \param ArgBegin - The beginning of the argument vector.
26///
27/// \param ArgEnd - The end of the argument vector.
28///
29/// \param PCHContainerOps - The PCHContainerOperations to use for loading and
30/// creating modules.
31///
32/// \param Diags - The diagnostics engine to use for reporting errors; its
33/// lifetime is expected to extend past that of the returned ASTUnit.
34///
35/// \param ResourceFilesPath - The path to the compiler resource files.
36///
37/// \param StorePreamblesInMemory - Whether to store PCH in memory. If false,
38/// PCH are stored in temporary files.
39///
40/// \param PreambleStoragePath - The path to a directory, in which to create
41/// temporary PCH files. If empty, the default system temporary directory is
42/// used. This parameter is ignored if \p StorePreamblesInMemory is true.
43///
44/// \param ModuleFormat - If provided, uses the specific module format.
45///
46/// \param ErrAST - If non-null and parsing failed without any AST to return
47/// (e.g. because the PCH could not be loaded), this accepts the ASTUnit
48/// mainly to allow the caller to see the diagnostics.
49///
50/// \param VFS - A llvm::vfs::FileSystem to be used for all file accesses.
51/// Note that preamble is saved to a temporary directory on a RealFileSystem,
52/// so in order for it to be loaded correctly, VFS should have access to
53/// it(i.e., be an overlay over RealFileSystem). RealFileSystem will be used
54/// if \p VFS is nullptr.
55///
56// FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we
57// shouldn't need to specify them at construction time.
58std::unique_ptr<ASTUnit> clang::CreateASTUnitFromCommandLine(
59 const char **ArgBegin, const char **ArgEnd,
60 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
61 std::shared_ptr<DiagnosticOptions> DiagOpts,
62 IntrusiveRefCntPtr<DiagnosticsEngine> Diags, StringRef ResourceFilesPath,
63 bool StorePreamblesInMemory, StringRef PreambleStoragePath,
64 bool OnlyLocalDecls, CaptureDiagsKind CaptureDiagnostics,
65 ArrayRef<ASTUnit::RemappedFile> RemappedFiles,
66 bool RemappedFilesKeepOriginalName, unsigned PrecompilePreambleAfterNParses,
67 TranslationUnitKind TUKind, bool CacheCodeCompletionResults,
68 bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors,
69 SkipFunctionBodiesScope SkipFunctionBodies, bool SingleFileParse,
70 bool UserFilesAreVolatile, bool ForSerialization,
71 bool RetainExcludedConditionalBlocks, std::optional<StringRef> ModuleFormat,
72 std::unique_ptr<ASTUnit> *ErrAST,
73 IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
74 assert(Diags.get() && "no DiagnosticsEngine was provided");
75
76 // If no VFS was provided, create one that tracks the physical file system.
77 // If '-working-directory' was passed as an argument, 'createInvocation' will
78 // set this as the current working directory of the VFS.
79 if (!VFS)
80 VFS = llvm::vfs::createPhysicalFileSystem();
81
82 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
83
84 std::shared_ptr<CompilerInvocation> CI;
85
86 {
87 CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags,
88 &StoredDiagnostics, nullptr);
89
90 CreateInvocationOptions CIOpts;
91 CIOpts.VFS = VFS;
92 CIOpts.Diags = Diags;
93 CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed?
94 CI = createInvocation(Args: llvm::ArrayRef(ArgBegin, ArgEnd), Opts: std::move(CIOpts));
95 if (!CI)
96 return nullptr;
97 }
98
99 // Override any files that need remapping
100 for (const auto &RemappedFile : RemappedFiles) {
101 CI->getPreprocessorOpts().addRemappedFile(From: RemappedFile.first,
102 To: RemappedFile.second);
103 }
104 PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
105 PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName;
106 PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors;
107 PPOpts.SingleFileParseMode = SingleFileParse;
108 PPOpts.RetainExcludedConditionalBlocks = RetainExcludedConditionalBlocks;
109
110 // Override the resources path.
111 CI->getHeaderSearchOpts().ResourceDir = std::string(ResourceFilesPath);
112
113 CI->getFrontendOpts().SkipFunctionBodies =
114 SkipFunctionBodies == SkipFunctionBodiesScope::PreambleAndMainFile;
115
116 if (ModuleFormat)
117 CI->getHeaderSearchOpts().ModuleFormat = std::string(*ModuleFormat);
118
119 // Create the AST unit.
120 std::unique_ptr<ASTUnit> AST;
121 AST.reset(p: new ASTUnit(false));
122 AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size();
123 AST->StoredDiagnostics.swap(RHS&: StoredDiagnostics);
124 ASTUnit::ConfigureDiags(Diags, AST&: *AST, CaptureDiagnostics);
125 AST->DiagOpts = DiagOpts;
126 AST->Diagnostics = Diags;
127 AST->FileSystemOpts = CI->getFileSystemOpts();
128 AST->CodeGenOpts = std::make_unique<CodeGenOptions>(args&: CI->getCodeGenOpts());
129 VFS = createVFSFromCompilerInvocation(CI: *CI, Diags&: *Diags, BaseFS: VFS);
130 AST->FileMgr =
131 llvm::makeIntrusiveRefCnt<FileManager>(A&: AST->FileSystemOpts, A&: VFS);
132 AST->StorePreamblesInMemory = StorePreamblesInMemory;
133 AST->PreambleStoragePath = PreambleStoragePath;
134 AST->ModCache = createCrossProcessModuleCache();
135 AST->OnlyLocalDecls = OnlyLocalDecls;
136 AST->CaptureDiagnostics = CaptureDiagnostics;
137 AST->TUKind = TUKind;
138 AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults;
139 AST->IncludeBriefCommentsInCodeCompletion =
140 IncludeBriefCommentsInCodeCompletion;
141 AST->UserFilesAreVolatile = UserFilesAreVolatile;
142 AST->Invocation = CI;
143 AST->SkipFunctionBodies = SkipFunctionBodies;
144 if (ForSerialization)
145 AST->WriterData.reset(
146 p: new ASTUnit::ASTWriterData(*AST->ModCache, *AST->CodeGenOpts));
147 // Zero out now to ease cleanup during crash recovery.
148 CI = nullptr;
149 Diags = nullptr;
150
151 // Recover resources if we crash before exiting this method.
152 llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> ASTUnitCleanup(AST.get());
153
154 if (AST->LoadFromCompilerInvocation(PCHContainerOps: std::move(PCHContainerOps),
155 PrecompilePreambleAfterNParses, VFS)) {
156 // Some error occurred, if caller wants to examine diagnostics, pass it the
157 // ASTUnit.
158 if (ErrAST) {
159 AST->StoredDiagnostics.swap(RHS&: AST->FailedParseDiagnostics);
160 ErrAST->swap(u&: AST);
161 }
162 return nullptr;
163 }
164
165 return AST;
166}
167