xref: /minix3/external/bsd/llvm/dist/clang/lib/Tooling/JSONCompilationDatabase.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1f4a2713aSLionel Sambuc //===--- JSONCompilationDatabase.cpp - ------------------------------------===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc //                     The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc //  This file contains the implementation of the JSONCompilationDatabase.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc 
14f4a2713aSLionel Sambuc #include "clang/Tooling/JSONCompilationDatabase.h"
15f4a2713aSLionel Sambuc #include "clang/Tooling/CompilationDatabase.h"
16f4a2713aSLionel Sambuc #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
17f4a2713aSLionel Sambuc #include "clang/Tooling/Tooling.h"
18f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
19f4a2713aSLionel Sambuc #include "llvm/Support/Path.h"
20*0a6a1f1dSLionel Sambuc #include <system_error>
21f4a2713aSLionel Sambuc 
22f4a2713aSLionel Sambuc namespace clang {
23f4a2713aSLionel Sambuc namespace tooling {
24f4a2713aSLionel Sambuc 
25f4a2713aSLionel Sambuc namespace {
26f4a2713aSLionel Sambuc 
27f4a2713aSLionel Sambuc /// \brief A parser for escaped strings of command line arguments.
28f4a2713aSLionel Sambuc ///
29f4a2713aSLionel Sambuc /// Assumes \-escaping for quoted arguments (see the documentation of
30f4a2713aSLionel Sambuc /// unescapeCommandLine(...)).
31f4a2713aSLionel Sambuc class CommandLineArgumentParser {
32f4a2713aSLionel Sambuc  public:
CommandLineArgumentParser(StringRef CommandLine)33f4a2713aSLionel Sambuc   CommandLineArgumentParser(StringRef CommandLine)
34f4a2713aSLionel Sambuc       : Input(CommandLine), Position(Input.begin()-1) {}
35f4a2713aSLionel Sambuc 
parse()36f4a2713aSLionel Sambuc   std::vector<std::string> parse() {
37f4a2713aSLionel Sambuc     bool HasMoreInput = true;
38f4a2713aSLionel Sambuc     while (HasMoreInput && nextNonWhitespace()) {
39f4a2713aSLionel Sambuc       std::string Argument;
40f4a2713aSLionel Sambuc       HasMoreInput = parseStringInto(Argument);
41f4a2713aSLionel Sambuc       CommandLine.push_back(Argument);
42f4a2713aSLionel Sambuc     }
43f4a2713aSLionel Sambuc     return CommandLine;
44f4a2713aSLionel Sambuc   }
45f4a2713aSLionel Sambuc 
46f4a2713aSLionel Sambuc  private:
47f4a2713aSLionel Sambuc   // All private methods return true if there is more input available.
48f4a2713aSLionel Sambuc 
parseStringInto(std::string & String)49f4a2713aSLionel Sambuc   bool parseStringInto(std::string &String) {
50f4a2713aSLionel Sambuc     do {
51f4a2713aSLionel Sambuc       if (*Position == '"') {
52f4a2713aSLionel Sambuc         if (!parseDoubleQuotedStringInto(String)) return false;
53f4a2713aSLionel Sambuc       } else if (*Position == '\'') {
54f4a2713aSLionel Sambuc         if (!parseSingleQuotedStringInto(String)) return false;
55f4a2713aSLionel Sambuc       } else {
56f4a2713aSLionel Sambuc         if (!parseFreeStringInto(String)) return false;
57f4a2713aSLionel Sambuc       }
58f4a2713aSLionel Sambuc     } while (*Position != ' ');
59f4a2713aSLionel Sambuc     return true;
60f4a2713aSLionel Sambuc   }
61f4a2713aSLionel Sambuc 
parseDoubleQuotedStringInto(std::string & String)62f4a2713aSLionel Sambuc   bool parseDoubleQuotedStringInto(std::string &String) {
63f4a2713aSLionel Sambuc     if (!next()) return false;
64f4a2713aSLionel Sambuc     while (*Position != '"') {
65f4a2713aSLionel Sambuc       if (!skipEscapeCharacter()) return false;
66f4a2713aSLionel Sambuc       String.push_back(*Position);
67f4a2713aSLionel Sambuc       if (!next()) return false;
68f4a2713aSLionel Sambuc     }
69f4a2713aSLionel Sambuc     return next();
70f4a2713aSLionel Sambuc   }
71f4a2713aSLionel Sambuc 
parseSingleQuotedStringInto(std::string & String)72f4a2713aSLionel Sambuc   bool parseSingleQuotedStringInto(std::string &String) {
73f4a2713aSLionel Sambuc     if (!next()) return false;
74f4a2713aSLionel Sambuc     while (*Position != '\'') {
75f4a2713aSLionel Sambuc       String.push_back(*Position);
76f4a2713aSLionel Sambuc       if (!next()) return false;
77f4a2713aSLionel Sambuc     }
78f4a2713aSLionel Sambuc     return next();
79f4a2713aSLionel Sambuc   }
80f4a2713aSLionel Sambuc 
parseFreeStringInto(std::string & String)81f4a2713aSLionel Sambuc   bool parseFreeStringInto(std::string &String) {
82f4a2713aSLionel Sambuc     do {
83f4a2713aSLionel Sambuc       if (!skipEscapeCharacter()) return false;
84f4a2713aSLionel Sambuc       String.push_back(*Position);
85f4a2713aSLionel Sambuc       if (!next()) return false;
86f4a2713aSLionel Sambuc     } while (*Position != ' ' && *Position != '"' && *Position != '\'');
87f4a2713aSLionel Sambuc     return true;
88f4a2713aSLionel Sambuc   }
89f4a2713aSLionel Sambuc 
skipEscapeCharacter()90f4a2713aSLionel Sambuc   bool skipEscapeCharacter() {
91f4a2713aSLionel Sambuc     if (*Position == '\\') {
92f4a2713aSLionel Sambuc       return next();
93f4a2713aSLionel Sambuc     }
94f4a2713aSLionel Sambuc     return true;
95f4a2713aSLionel Sambuc   }
96f4a2713aSLionel Sambuc 
nextNonWhitespace()97f4a2713aSLionel Sambuc   bool nextNonWhitespace() {
98f4a2713aSLionel Sambuc     do {
99f4a2713aSLionel Sambuc       if (!next()) return false;
100f4a2713aSLionel Sambuc     } while (*Position == ' ');
101f4a2713aSLionel Sambuc     return true;
102f4a2713aSLionel Sambuc   }
103f4a2713aSLionel Sambuc 
next()104f4a2713aSLionel Sambuc   bool next() {
105f4a2713aSLionel Sambuc     ++Position;
106f4a2713aSLionel Sambuc     return Position != Input.end();
107f4a2713aSLionel Sambuc   }
108f4a2713aSLionel Sambuc 
109f4a2713aSLionel Sambuc   const StringRef Input;
110f4a2713aSLionel Sambuc   StringRef::iterator Position;
111f4a2713aSLionel Sambuc   std::vector<std::string> CommandLine;
112f4a2713aSLionel Sambuc };
113f4a2713aSLionel Sambuc 
unescapeCommandLine(StringRef EscapedCommandLine)114f4a2713aSLionel Sambuc std::vector<std::string> unescapeCommandLine(
115f4a2713aSLionel Sambuc     StringRef EscapedCommandLine) {
116f4a2713aSLionel Sambuc   CommandLineArgumentParser parser(EscapedCommandLine);
117f4a2713aSLionel Sambuc   return parser.parse();
118f4a2713aSLionel Sambuc }
119f4a2713aSLionel Sambuc 
120f4a2713aSLionel Sambuc class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
121*0a6a1f1dSLionel Sambuc   std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)122*0a6a1f1dSLionel Sambuc   loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
123f4a2713aSLionel Sambuc     SmallString<1024> JSONDatabasePath(Directory);
124f4a2713aSLionel Sambuc     llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
125*0a6a1f1dSLionel Sambuc     std::unique_ptr<CompilationDatabase> Database(
126f4a2713aSLionel Sambuc         JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
127f4a2713aSLionel Sambuc     if (!Database)
128*0a6a1f1dSLionel Sambuc       return nullptr;
129*0a6a1f1dSLionel Sambuc     return Database;
130f4a2713aSLionel Sambuc   }
131f4a2713aSLionel Sambuc };
132f4a2713aSLionel Sambuc 
133f4a2713aSLionel Sambuc } // end namespace
134f4a2713aSLionel Sambuc 
135f4a2713aSLionel Sambuc // Register the JSONCompilationDatabasePlugin with the
136f4a2713aSLionel Sambuc // CompilationDatabasePluginRegistry using this statically initialized variable.
137f4a2713aSLionel Sambuc static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
138f4a2713aSLionel Sambuc X("json-compilation-database", "Reads JSON formatted compilation databases");
139f4a2713aSLionel Sambuc 
140f4a2713aSLionel Sambuc // This anchor is used to force the linker to link in the generated object file
141f4a2713aSLionel Sambuc // and thus register the JSONCompilationDatabasePlugin.
142f4a2713aSLionel Sambuc volatile int JSONAnchorSource = 0;
143f4a2713aSLionel Sambuc 
144*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase>
loadFromFile(StringRef FilePath,std::string & ErrorMessage)145f4a2713aSLionel Sambuc JSONCompilationDatabase::loadFromFile(StringRef FilePath,
146f4a2713aSLionel Sambuc                                       std::string &ErrorMessage) {
147*0a6a1f1dSLionel Sambuc   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
148*0a6a1f1dSLionel Sambuc       llvm::MemoryBuffer::getFile(FilePath);
149*0a6a1f1dSLionel Sambuc   if (std::error_code Result = DatabaseBuffer.getError()) {
150f4a2713aSLionel Sambuc     ErrorMessage = "Error while opening JSON database: " + Result.message();
151*0a6a1f1dSLionel Sambuc     return nullptr;
152f4a2713aSLionel Sambuc   }
153*0a6a1f1dSLionel Sambuc   std::unique_ptr<JSONCompilationDatabase> Database(
154*0a6a1f1dSLionel Sambuc       new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
155f4a2713aSLionel Sambuc   if (!Database->parse(ErrorMessage))
156*0a6a1f1dSLionel Sambuc     return nullptr;
157*0a6a1f1dSLionel Sambuc   return Database;
158f4a2713aSLionel Sambuc }
159f4a2713aSLionel Sambuc 
160*0a6a1f1dSLionel Sambuc std::unique_ptr<JSONCompilationDatabase>
loadFromBuffer(StringRef DatabaseString,std::string & ErrorMessage)161f4a2713aSLionel Sambuc JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
162f4a2713aSLionel Sambuc                                         std::string &ErrorMessage) {
163*0a6a1f1dSLionel Sambuc   std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
164f4a2713aSLionel Sambuc       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
165*0a6a1f1dSLionel Sambuc   std::unique_ptr<JSONCompilationDatabase> Database(
166*0a6a1f1dSLionel Sambuc       new JSONCompilationDatabase(std::move(DatabaseBuffer)));
167f4a2713aSLionel Sambuc   if (!Database->parse(ErrorMessage))
168*0a6a1f1dSLionel Sambuc     return nullptr;
169*0a6a1f1dSLionel Sambuc   return Database;
170f4a2713aSLionel Sambuc }
171f4a2713aSLionel Sambuc 
172f4a2713aSLionel Sambuc std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const173f4a2713aSLionel Sambuc JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
174f4a2713aSLionel Sambuc   SmallString<128> NativeFilePath;
175f4a2713aSLionel Sambuc   llvm::sys::path::native(FilePath, NativeFilePath);
176*0a6a1f1dSLionel Sambuc 
177f4a2713aSLionel Sambuc   std::string Error;
178f4a2713aSLionel Sambuc   llvm::raw_string_ostream ES(Error);
179f4a2713aSLionel Sambuc   StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
180f4a2713aSLionel Sambuc   if (Match.empty())
181f4a2713aSLionel Sambuc     return std::vector<CompileCommand>();
182f4a2713aSLionel Sambuc   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
183f4a2713aSLionel Sambuc     CommandsRefI = IndexByFile.find(Match);
184f4a2713aSLionel Sambuc   if (CommandsRefI == IndexByFile.end())
185f4a2713aSLionel Sambuc     return std::vector<CompileCommand>();
186f4a2713aSLionel Sambuc   std::vector<CompileCommand> Commands;
187f4a2713aSLionel Sambuc   getCommands(CommandsRefI->getValue(), Commands);
188f4a2713aSLionel Sambuc   return Commands;
189f4a2713aSLionel Sambuc }
190f4a2713aSLionel Sambuc 
191f4a2713aSLionel Sambuc std::vector<std::string>
getAllFiles() const192f4a2713aSLionel Sambuc JSONCompilationDatabase::getAllFiles() const {
193f4a2713aSLionel Sambuc   std::vector<std::string> Result;
194f4a2713aSLionel Sambuc 
195f4a2713aSLionel Sambuc   llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
196f4a2713aSLionel Sambuc     CommandsRefI = IndexByFile.begin();
197f4a2713aSLionel Sambuc   const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
198f4a2713aSLionel Sambuc     CommandsRefEnd = IndexByFile.end();
199f4a2713aSLionel Sambuc   for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
200f4a2713aSLionel Sambuc     Result.push_back(CommandsRefI->first().str());
201f4a2713aSLionel Sambuc   }
202f4a2713aSLionel Sambuc 
203f4a2713aSLionel Sambuc   return Result;
204f4a2713aSLionel Sambuc }
205f4a2713aSLionel Sambuc 
206f4a2713aSLionel Sambuc std::vector<CompileCommand>
getAllCompileCommands() const207f4a2713aSLionel Sambuc JSONCompilationDatabase::getAllCompileCommands() const {
208f4a2713aSLionel Sambuc   std::vector<CompileCommand> Commands;
209f4a2713aSLionel Sambuc   for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
210f4a2713aSLionel Sambuc         CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
211f4a2713aSLionel Sambuc       CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
212f4a2713aSLionel Sambuc     getCommands(CommandsRefI->getValue(), Commands);
213f4a2713aSLionel Sambuc   }
214f4a2713aSLionel Sambuc   return Commands;
215f4a2713aSLionel Sambuc }
216f4a2713aSLionel Sambuc 
getCommands(ArrayRef<CompileCommandRef> CommandsRef,std::vector<CompileCommand> & Commands) const217f4a2713aSLionel Sambuc void JSONCompilationDatabase::getCommands(
218f4a2713aSLionel Sambuc                                   ArrayRef<CompileCommandRef> CommandsRef,
219f4a2713aSLionel Sambuc                                   std::vector<CompileCommand> &Commands) const {
220f4a2713aSLionel Sambuc   for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
221f4a2713aSLionel Sambuc     SmallString<8> DirectoryStorage;
222f4a2713aSLionel Sambuc     SmallString<1024> CommandStorage;
223f4a2713aSLionel Sambuc     Commands.push_back(CompileCommand(
224f4a2713aSLionel Sambuc       // FIXME: Escape correctly:
225f4a2713aSLionel Sambuc       CommandsRef[I].first->getValue(DirectoryStorage),
226f4a2713aSLionel Sambuc       unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
227f4a2713aSLionel Sambuc   }
228f4a2713aSLionel Sambuc }
229f4a2713aSLionel Sambuc 
parse(std::string & ErrorMessage)230f4a2713aSLionel Sambuc bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
231f4a2713aSLionel Sambuc   llvm::yaml::document_iterator I = YAMLStream.begin();
232f4a2713aSLionel Sambuc   if (I == YAMLStream.end()) {
233f4a2713aSLionel Sambuc     ErrorMessage = "Error while parsing YAML.";
234f4a2713aSLionel Sambuc     return false;
235f4a2713aSLionel Sambuc   }
236f4a2713aSLionel Sambuc   llvm::yaml::Node *Root = I->getRoot();
237*0a6a1f1dSLionel Sambuc   if (!Root) {
238f4a2713aSLionel Sambuc     ErrorMessage = "Error while parsing YAML.";
239f4a2713aSLionel Sambuc     return false;
240f4a2713aSLionel Sambuc   }
241f4a2713aSLionel Sambuc   llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
242*0a6a1f1dSLionel Sambuc   if (!Array) {
243f4a2713aSLionel Sambuc     ErrorMessage = "Expected array.";
244f4a2713aSLionel Sambuc     return false;
245f4a2713aSLionel Sambuc   }
246f4a2713aSLionel Sambuc   for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
247f4a2713aSLionel Sambuc                                           AE = Array->end();
248f4a2713aSLionel Sambuc        AI != AE; ++AI) {
249f4a2713aSLionel Sambuc     llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
250*0a6a1f1dSLionel Sambuc     if (!Object) {
251f4a2713aSLionel Sambuc       ErrorMessage = "Expected object.";
252f4a2713aSLionel Sambuc       return false;
253f4a2713aSLionel Sambuc     }
254*0a6a1f1dSLionel Sambuc     llvm::yaml::ScalarNode *Directory = nullptr;
255*0a6a1f1dSLionel Sambuc     llvm::yaml::ScalarNode *Command = nullptr;
256*0a6a1f1dSLionel Sambuc     llvm::yaml::ScalarNode *File = nullptr;
257f4a2713aSLionel Sambuc     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
258f4a2713aSLionel Sambuc                                            KVE = Object->end();
259f4a2713aSLionel Sambuc          KVI != KVE; ++KVI) {
260f4a2713aSLionel Sambuc       llvm::yaml::Node *Value = (*KVI).getValue();
261*0a6a1f1dSLionel Sambuc       if (!Value) {
262f4a2713aSLionel Sambuc         ErrorMessage = "Expected value.";
263f4a2713aSLionel Sambuc         return false;
264f4a2713aSLionel Sambuc       }
265f4a2713aSLionel Sambuc       llvm::yaml::ScalarNode *ValueString =
266f4a2713aSLionel Sambuc           dyn_cast<llvm::yaml::ScalarNode>(Value);
267*0a6a1f1dSLionel Sambuc       if (!ValueString) {
268f4a2713aSLionel Sambuc         ErrorMessage = "Expected string as value.";
269f4a2713aSLionel Sambuc         return false;
270f4a2713aSLionel Sambuc       }
271f4a2713aSLionel Sambuc       llvm::yaml::ScalarNode *KeyString =
272f4a2713aSLionel Sambuc           dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
273*0a6a1f1dSLionel Sambuc       if (!KeyString) {
274f4a2713aSLionel Sambuc         ErrorMessage = "Expected strings as key.";
275f4a2713aSLionel Sambuc         return false;
276f4a2713aSLionel Sambuc       }
277f4a2713aSLionel Sambuc       SmallString<8> KeyStorage;
278f4a2713aSLionel Sambuc       if (KeyString->getValue(KeyStorage) == "directory") {
279f4a2713aSLionel Sambuc         Directory = ValueString;
280f4a2713aSLionel Sambuc       } else if (KeyString->getValue(KeyStorage) == "command") {
281f4a2713aSLionel Sambuc         Command = ValueString;
282f4a2713aSLionel Sambuc       } else if (KeyString->getValue(KeyStorage) == "file") {
283f4a2713aSLionel Sambuc         File = ValueString;
284f4a2713aSLionel Sambuc       } else {
285f4a2713aSLionel Sambuc         ErrorMessage = ("Unknown key: \"" +
286f4a2713aSLionel Sambuc                         KeyString->getRawValue() + "\"").str();
287f4a2713aSLionel Sambuc         return false;
288f4a2713aSLionel Sambuc       }
289f4a2713aSLionel Sambuc     }
290f4a2713aSLionel Sambuc     if (!File) {
291f4a2713aSLionel Sambuc       ErrorMessage = "Missing key: \"file\".";
292f4a2713aSLionel Sambuc       return false;
293f4a2713aSLionel Sambuc     }
294f4a2713aSLionel Sambuc     if (!Command) {
295f4a2713aSLionel Sambuc       ErrorMessage = "Missing key: \"command\".";
296f4a2713aSLionel Sambuc       return false;
297f4a2713aSLionel Sambuc     }
298f4a2713aSLionel Sambuc     if (!Directory) {
299f4a2713aSLionel Sambuc       ErrorMessage = "Missing key: \"directory\".";
300f4a2713aSLionel Sambuc       return false;
301f4a2713aSLionel Sambuc     }
302f4a2713aSLionel Sambuc     SmallString<8> FileStorage;
303f4a2713aSLionel Sambuc     StringRef FileName = File->getValue(FileStorage);
304f4a2713aSLionel Sambuc     SmallString<128> NativeFilePath;
305f4a2713aSLionel Sambuc     if (llvm::sys::path::is_relative(FileName)) {
306f4a2713aSLionel Sambuc       SmallString<8> DirectoryStorage;
307f4a2713aSLionel Sambuc       SmallString<128> AbsolutePath(
308f4a2713aSLionel Sambuc           Directory->getValue(DirectoryStorage));
309f4a2713aSLionel Sambuc       llvm::sys::path::append(AbsolutePath, FileName);
310f4a2713aSLionel Sambuc       llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
311f4a2713aSLionel Sambuc     } else {
312f4a2713aSLionel Sambuc       llvm::sys::path::native(FileName, NativeFilePath);
313f4a2713aSLionel Sambuc     }
314f4a2713aSLionel Sambuc     IndexByFile[NativeFilePath].push_back(
315f4a2713aSLionel Sambuc         CompileCommandRef(Directory, Command));
316f4a2713aSLionel Sambuc     MatchTrie.insert(NativeFilePath.str());
317f4a2713aSLionel Sambuc   }
318f4a2713aSLionel Sambuc   return true;
319f4a2713aSLionel Sambuc }
320f4a2713aSLionel Sambuc 
321f4a2713aSLionel Sambuc } // end namespace tooling
322f4a2713aSLionel Sambuc } // end namespace clang
323