-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclang-ast.cc
168 lines (129 loc) · 4.83 KB
/
clang-ast.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// clang-ast.cc
// Code for `clang-ast.h`.
#include "clang-ast.h" // this module
#include "clang/Basic/Diagnostic.h" // clang::DiagnosticsEngine
#include "clang/Basic/DiagnosticOptions.h" // clang::DiagnosticOptions
#include "clang/Basic/Version.h" // CLANG_VERSION_MAJOR
#include "clang/Frontend/ASTUnit.h" // clang::ASTUnit
#include "clang/Frontend/CompilerInstance.h" // clang::CompilerInstance
#include "clang/Frontend/Utils.h" // clang::createInvocationFromCommandLine
#include "clang/Serialization/PCHContainerOperations.h" // clang::PCHContainerOperations
#include "smbase/exc.h" // smbase::xmessage
using namespace smbase;
// ----------------------------- ClangAST ------------------------------
ClangAST::~ClangAST()
{}
ClangAST::ClangAST()
: m_compilerInvocation(),
m_primarySourceFileName(),
m_ast()
{}
ClangAST::ClangAST(std::vector<std::string> const &fnameAndArgs)
: ClangAST()
{
if (!parseCommandLine(fnameAndArgs)) {
xmessage("Failed to parse command line.");
}
if (!parseSourceCode()) {
xmessage("Failed to parse source code.");
}
}
bool ClangAST::parseCommandLine(
std::vector<std::string> const &fnameAndArgs)
{
// Copy the arguments into a vector of char pointers since that is
// what 'createInvocationFromCommandLine' wants.
std::vector<char const *> commandLine;
{
// Path to the 'clang' binary that I am behaving like. This path is
// used to compute the location of compiler headers like stddef.h.
commandLine.push_back(CLANG_LLVM_INSTALL_DIR "/bin/clang");
for (std::string const &s : fnameAndArgs) {
commandLine.push_back(s.c_str());
}
}
// Parse the command line options.
m_compilerInvocation =
#if CLANG_VERSION_MAJOR <= 14
clang::createInvocationFromCommandLine(llvm::makeArrayRef(commandLine));
#else
clang::createInvocation(llvm::ArrayRef(commandLine));
#endif
if (!m_compilerInvocation) {
// Command line parsing errors have already been printed.
return false;
}
// Get the name of the primary source file.
if (m_compilerInvocation->getFrontendOpts().Inputs.size() != 1) {
// Unfortunately, this message is never seen because
// createInvocation will choke first (and in the multiple input
// case, spew a huge error message).
cerr << "expected exactly 1 input file, not "
<< m_compilerInvocation->getFrontendOpts().Inputs.size()
<< "\n";
return false;
}
m_primarySourceFileName =
m_compilerInvocation->getFrontendOpts().Inputs[0].getFile().str();
return true;
}
bool ClangAST::parseSourceCode(clang::FrontendAction *feAction)
{
// Boilerplate.
std::shared_ptr<clang::PCHContainerOperations> pchContainerOps(
new clang::PCHContainerOperations());
// Arrange to print the option names with emitted diagnostics.
clang::DiagnosticOptions *diagnosticOptions =
&(m_compilerInvocation->getDiagnosticOpts());
diagnosticOptions->ShowOptionNames = true;
// The diagnostics engine created this way will simply print all
// warnings and errors to stderr as they occur, and also maintain a
// count of each.
clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnosticsEngine(
clang::CompilerInstance::createDiagnostics(
// Because we are passing a `DiagnosticOptions` here, rather than
// a `DiagnosticConsumer`, we retain ownership of it.
diagnosticOptions));
// Run the Clang parser to produce an AST.
m_ast.reset(
clang::ASTUnit::LoadFromCompilerInvocationAction(
m_compilerInvocation,
pchContainerOps,
diagnosticsEngine,
feAction));
if (m_ast == nullptr) {
// Error messages should already have been printed.
return false;
}
if (diagnosticsEngine->getNumErrors() > 0) {
// The errors should have been printed already.
return false;
}
return true;
}
clang::ASTUnit *ClangAST::getASTUnit()
{
return m_ast.get();
}
clang::ASTContext &ClangAST::getASTContext()
{
return m_ast->getASTContext();
}
// --------------------------- ClangASTUtil ----------------------------
ClangASTUtil::~ClangASTUtil()
{}
ClangASTUtil::ClangASTUtil(std::vector<std::string> const &fnameAndArgs)
: ClangAST(fnameAndArgs),
ClangUtil(m_ast->getASTContext())
{}
// ----------------------- ClangASTUtilTempFile ------------------------
ClangASTUtilTempFile::~ClangASTUtilTempFile()
{}
// Clang has a virtual file system that could be used to avoid actually
// hitting the disk here, but after a quick look, I decided it appeared
// to be more trouble than I wanted to deal with.
ClangASTUtilTempFile::ClangASTUtilTempFile(std::string const &source)
: TemporaryFile("cautf", "cc", source),
ClangASTUtil({TemporaryFile::getFname()})
{}
// EOF