147c245a5SManuel Klimek //===--- CompilationDatabase.cpp - ----------------------------------------===//
247c245a5SManuel Klimek //
347c245a5SManuel Klimek //                     The LLVM Compiler Infrastructure
447c245a5SManuel Klimek //
547c245a5SManuel Klimek // This file is distributed under the University of Illinois Open Source
647c245a5SManuel Klimek // License. See LICENSE.TXT for details.
747c245a5SManuel Klimek //
847c245a5SManuel Klimek //===----------------------------------------------------------------------===//
947c245a5SManuel Klimek //
106ed1f85cSDaniel Jasper //  This file contains implementations of the CompilationDatabase base class
116ed1f85cSDaniel Jasper //  and the FixedCompilationDatabase.
1247c245a5SManuel Klimek //
1347c245a5SManuel Klimek //===----------------------------------------------------------------------===//
1447c245a5SManuel Klimek 
1547c245a5SManuel Klimek #include "clang/Tooling/CompilationDatabase.h"
16eb56f4feSEdwin Vane #include "clang/Basic/Diagnostic.h"
17eb56f4feSEdwin Vane #include "clang/Driver/Action.h"
185553d0d4SChandler Carruth #include "clang/Driver/Compilation.h"
19eb56f4feSEdwin Vane #include "clang/Driver/Driver.h"
20eb56f4feSEdwin Vane #include "clang/Driver/DriverDiagnostic.h"
21eb56f4feSEdwin Vane #include "clang/Driver/Job.h"
22eb56f4feSEdwin Vane #include "clang/Frontend/TextDiagnosticPrinter.h"
235553d0d4SChandler Carruth #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
245553d0d4SChandler Carruth #include "clang/Tooling/Tooling.h"
255553d0d4SChandler Carruth #include "llvm/ADT/SmallString.h"
26eb56f4feSEdwin Vane #include "llvm/Option/Arg.h"
275553d0d4SChandler Carruth #include "llvm/Support/Host.h"
285553d0d4SChandler Carruth #include "llvm/Support/Path.h"
295553d0d4SChandler Carruth #include "llvm/Support/system_error.h"
305553d0d4SChandler Carruth #include <sstream>
31eb56f4feSEdwin Vane 
3247c245a5SManuel Klimek namespace clang {
3347c245a5SManuel Klimek namespace tooling {
3447c245a5SManuel Klimek 
3547c245a5SManuel Klimek CompilationDatabase::~CompilationDatabase() {}
3647c245a5SManuel Klimek 
3747c245a5SManuel Klimek CompilationDatabase *
3847c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
3947c245a5SManuel Klimek                                        std::string &ErrorMessage) {
406ed1f85cSDaniel Jasper   std::stringstream ErrorStream;
416ed1f85cSDaniel Jasper   for (CompilationDatabasePluginRegistry::iterator
426ed1f85cSDaniel Jasper        It = CompilationDatabasePluginRegistry::begin(),
436ed1f85cSDaniel Jasper        Ie = CompilationDatabasePluginRegistry::end();
446ed1f85cSDaniel Jasper        It != Ie; ++It) {
456ed1f85cSDaniel Jasper     std::string DatabaseErrorMessage;
46b8984329SAhmed Charles     std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
476ed1f85cSDaniel Jasper     if (CompilationDatabase *DB =
486ed1f85cSDaniel Jasper         Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
496ed1f85cSDaniel Jasper       return DB;
506ed1f85cSDaniel Jasper     else
516ed1f85cSDaniel Jasper       ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
5247c245a5SManuel Klimek   }
536ed1f85cSDaniel Jasper   ErrorMessage = ErrorStream.str();
546ed1f85cSDaniel Jasper   return NULL;
5547c245a5SManuel Klimek }
5647c245a5SManuel Klimek 
57617f5269SArnaud A. de Grandmaison static CompilationDatabase *
586ed1f85cSDaniel Jasper findCompilationDatabaseFromDirectory(StringRef Directory,
596ed1f85cSDaniel Jasper                                      std::string &ErrorMessage) {
606ed1f85cSDaniel Jasper   std::stringstream ErrorStream;
6174351ff4SDaniel Jasper   bool HasErrorMessage = false;
62617f5269SArnaud A. de Grandmaison   while (!Directory.empty()) {
63617f5269SArnaud A. de Grandmaison     std::string LoadErrorMessage;
64617f5269SArnaud A. de Grandmaison 
65617f5269SArnaud A. de Grandmaison     if (CompilationDatabase *DB =
66617f5269SArnaud A. de Grandmaison            CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
67617f5269SArnaud A. de Grandmaison       return DB;
6874351ff4SDaniel Jasper 
6974351ff4SDaniel Jasper     if (!HasErrorMessage) {
706ed1f85cSDaniel Jasper       ErrorStream << "No compilation database found in " << Directory.str()
7174351ff4SDaniel Jasper                   << " or any parent directory\n" << LoadErrorMessage;
7274351ff4SDaniel Jasper       HasErrorMessage = true;
7374351ff4SDaniel Jasper     }
74617f5269SArnaud A. de Grandmaison 
75617f5269SArnaud A. de Grandmaison     Directory = llvm::sys::path::parent_path(Directory);
76617f5269SArnaud A. de Grandmaison   }
776ed1f85cSDaniel Jasper   ErrorMessage = ErrorStream.str();
78617f5269SArnaud A. de Grandmaison   return NULL;
79617f5269SArnaud A. de Grandmaison }
80617f5269SArnaud A. de Grandmaison 
8165fd0e1fSManuel Klimek CompilationDatabase *
8265fd0e1fSManuel Klimek CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
8365fd0e1fSManuel Klimek                                           std::string &ErrorMessage) {
84f857950dSDmitri Gribenko   SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
8565fd0e1fSManuel Klimek   StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
86617f5269SArnaud A. de Grandmaison 
876ed1f85cSDaniel Jasper   CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory,
886ed1f85cSDaniel Jasper                                                                  ErrorMessage);
89617f5269SArnaud A. de Grandmaison 
90617f5269SArnaud A. de Grandmaison   if (!DB)
9165fd0e1fSManuel Klimek     ErrorMessage = ("Could not auto-detect compilation database for file \"" +
926ed1f85cSDaniel Jasper                    SourceFile + "\"\n" + ErrorMessage).str();
93617f5269SArnaud A. de Grandmaison   return DB;
94617f5269SArnaud A. de Grandmaison }
95617f5269SArnaud A. de Grandmaison 
96617f5269SArnaud A. de Grandmaison CompilationDatabase *
97617f5269SArnaud A. de Grandmaison CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
98617f5269SArnaud A. de Grandmaison                                              std::string &ErrorMessage) {
99f857950dSDmitri Gribenko   SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
100617f5269SArnaud A. de Grandmaison 
1016ed1f85cSDaniel Jasper   CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath,
1026ed1f85cSDaniel Jasper                                                                  ErrorMessage);
103617f5269SArnaud A. de Grandmaison 
104617f5269SArnaud A. de Grandmaison   if (!DB)
105617f5269SArnaud A. de Grandmaison     ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
1066ed1f85cSDaniel Jasper                    SourceDir + "\"\n" + ErrorMessage).str();
107617f5269SArnaud A. de Grandmaison   return DB;
10865fd0e1fSManuel Klimek }
10965fd0e1fSManuel Klimek 
1106ed1f85cSDaniel Jasper CompilationDatabasePlugin::~CompilationDatabasePlugin() {}
1116ed1f85cSDaniel Jasper 
112eb56f4feSEdwin Vane // Helper for recursively searching through a chain of actions and collecting
113eb56f4feSEdwin Vane // all inputs, direct and indirect, of compile jobs.
114eb56f4feSEdwin Vane struct CompileJobAnalyzer {
115eb56f4feSEdwin Vane   void run(const driver::Action *A) {
116eb56f4feSEdwin Vane     runImpl(A, false);
117eb56f4feSEdwin Vane   }
118eb56f4feSEdwin Vane 
119eb56f4feSEdwin Vane   SmallVector<std::string, 2> Inputs;
120eb56f4feSEdwin Vane 
121eb56f4feSEdwin Vane private:
122eb56f4feSEdwin Vane 
123eb56f4feSEdwin Vane   void runImpl(const driver::Action *A, bool Collect) {
124eb56f4feSEdwin Vane     bool CollectChildren = Collect;
125eb56f4feSEdwin Vane     switch (A->getKind()) {
126eb56f4feSEdwin Vane     case driver::Action::CompileJobClass:
127eb56f4feSEdwin Vane       CollectChildren = true;
128eb56f4feSEdwin Vane       break;
129eb56f4feSEdwin Vane 
130eb56f4feSEdwin Vane     case driver::Action::InputClass: {
131eb56f4feSEdwin Vane       if (Collect) {
132eb56f4feSEdwin Vane         const driver::InputAction *IA = cast<driver::InputAction>(A);
133eb56f4feSEdwin Vane         Inputs.push_back(IA->getInputArg().getSpelling());
134eb56f4feSEdwin Vane       }
135eb56f4feSEdwin Vane     } break;
136eb56f4feSEdwin Vane 
137eb56f4feSEdwin Vane     default:
138eb56f4feSEdwin Vane       // Don't care about others
139eb56f4feSEdwin Vane       ;
140eb56f4feSEdwin Vane     }
141eb56f4feSEdwin Vane 
142eb56f4feSEdwin Vane     for (driver::ActionList::const_iterator I = A->begin(), E = A->end();
143eb56f4feSEdwin Vane          I != E; ++I)
144eb56f4feSEdwin Vane       runImpl(*I, CollectChildren);
145eb56f4feSEdwin Vane   }
146eb56f4feSEdwin Vane };
147eb56f4feSEdwin Vane 
148eb56f4feSEdwin Vane // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
149eb56f4feSEdwin Vane // diagnostics from the driver and collects the option strings for those unused
150eb56f4feSEdwin Vane // options.
151eb56f4feSEdwin Vane class UnusedInputDiagConsumer : public DiagnosticConsumer {
152eb56f4feSEdwin Vane public:
153eb56f4feSEdwin Vane   UnusedInputDiagConsumer() : Other(0) {}
154eb56f4feSEdwin Vane 
155eb56f4feSEdwin Vane   // Useful for debugging, chain diagnostics to another consumer after
156eb56f4feSEdwin Vane   // recording for our own purposes.
157eb56f4feSEdwin Vane   UnusedInputDiagConsumer(DiagnosticConsumer *Other) : Other(Other) {}
158eb56f4feSEdwin Vane 
159eb56f4feSEdwin Vane   virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
160a798a9dbSCraig Topper                                 const Diagnostic &Info) override {
161eb56f4feSEdwin Vane     if (Info.getID() == clang::diag::warn_drv_input_file_unused) {
162eb56f4feSEdwin Vane       // Arg 1 for this diagnostic is the option that didn't get used.
163eb56f4feSEdwin Vane       UnusedInputs.push_back(Info.getArgStdStr(0));
164eb56f4feSEdwin Vane     }
165eb56f4feSEdwin Vane     if (Other)
166eb56f4feSEdwin Vane       Other->HandleDiagnostic(DiagLevel, Info);
167eb56f4feSEdwin Vane   }
168eb56f4feSEdwin Vane 
169eb56f4feSEdwin Vane   DiagnosticConsumer *Other;
170eb56f4feSEdwin Vane   SmallVector<std::string, 2> UnusedInputs;
171eb56f4feSEdwin Vane };
172eb56f4feSEdwin Vane 
173eb56f4feSEdwin Vane // Unary functor for asking "Given a StringRef S1, does there exist a string
174eb56f4feSEdwin Vane // S2 in Arr where S1 == S2?"
175eb56f4feSEdwin Vane struct MatchesAny {
176eb56f4feSEdwin Vane   MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
177eb56f4feSEdwin Vane   bool operator() (StringRef S) {
178eb56f4feSEdwin Vane     for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I)
179eb56f4feSEdwin Vane       if (*I == S)
180eb56f4feSEdwin Vane         return true;
181eb56f4feSEdwin Vane     return false;
182eb56f4feSEdwin Vane   }
183eb56f4feSEdwin Vane private:
184eb56f4feSEdwin Vane   ArrayRef<std::string> Arr;
185eb56f4feSEdwin Vane };
186eb56f4feSEdwin Vane 
187eb56f4feSEdwin Vane /// \brief Strips any positional args and possible argv[0] from a command-line
188eb56f4feSEdwin Vane /// provided by the user to construct a FixedCompilationDatabase.
189eb56f4feSEdwin Vane ///
190eb56f4feSEdwin Vane /// FixedCompilationDatabase requires a command line to be in this format as it
191eb56f4feSEdwin Vane /// constructs the command line for each file by appending the name of the file
192eb56f4feSEdwin Vane /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
193eb56f4feSEdwin Vane /// start of the command line although its value is not important as it's just
194eb56f4feSEdwin Vane /// ignored by the Driver invoked by the ClangTool using the
195eb56f4feSEdwin Vane /// FixedCompilationDatabase.
196eb56f4feSEdwin Vane ///
197eb56f4feSEdwin Vane /// FIXME: This functionality should probably be made available by
198eb56f4feSEdwin Vane /// clang::driver::Driver although what the interface should look like is not
199eb56f4feSEdwin Vane /// clear.
200eb56f4feSEdwin Vane ///
201eb56f4feSEdwin Vane /// \param[in] Args Args as provided by the user.
2025335cf33SNAKAMURA Takumi /// \return Resulting stripped command line.
2035335cf33SNAKAMURA Takumi ///          \li true if successful.
204eb56f4feSEdwin Vane ///          \li false if \c Args cannot be used for compilation jobs (e.g.
205eb56f4feSEdwin Vane ///          contains an option like -E or -version).
2063a4fe369SArtyom Skrobov static bool stripPositionalArgs(std::vector<const char *> Args,
207eb56f4feSEdwin Vane                                 std::vector<std::string> &Result) {
208eb56f4feSEdwin Vane   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
209eb56f4feSEdwin Vane   UnusedInputDiagConsumer DiagClient;
210eb56f4feSEdwin Vane   DiagnosticsEngine Diagnostics(
211eb56f4feSEdwin Vane       IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()),
212eb56f4feSEdwin Vane       &*DiagOpts, &DiagClient, false);
213eb56f4feSEdwin Vane 
214eb56f4feSEdwin Vane   // Neither clang executable nor default image name are required since the
215eb56f4feSEdwin Vane   // jobs the driver builds will not be executed.
216b8984329SAhmed Charles   std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
217eb56f4feSEdwin Vane       /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
218eb56f4feSEdwin Vane       /* DefaultImageName= */ "", Diagnostics));
219eb56f4feSEdwin Vane   NewDriver->setCheckInputsExist(false);
220eb56f4feSEdwin Vane 
221eb56f4feSEdwin Vane   // This becomes the new argv[0]. The value is actually not important as it
222eb56f4feSEdwin Vane   // isn't used for invoking Tools.
223eb56f4feSEdwin Vane   Args.insert(Args.begin(), "clang-tool");
224eb56f4feSEdwin Vane 
225eb56f4feSEdwin Vane   // By adding -c, we force the driver to treat compilation as the last phase.
226eb56f4feSEdwin Vane   // It will then issue warnings via Diagnostics about un-used options that
227eb56f4feSEdwin Vane   // would have been used for linking. If the user provided a compiler name as
228eb56f4feSEdwin Vane   // the original argv[0], this will be treated as a linker input thanks to
229eb56f4feSEdwin Vane   // insertng a new argv[0] above. All un-used options get collected by
230eb56f4feSEdwin Vane   // UnusedInputdiagConsumer and get stripped out later.
231eb56f4feSEdwin Vane   Args.push_back("-c");
232eb56f4feSEdwin Vane 
233eb56f4feSEdwin Vane   // Put a dummy C++ file on to ensure there's at least one compile job for the
234eb56f4feSEdwin Vane   // driver to construct. If the user specified some other argument that
235eb56f4feSEdwin Vane   // prevents compilation, e.g. -E or something like -version, we may still end
236eb56f4feSEdwin Vane   // up with no jobs but then this is the user's fault.
237eb56f4feSEdwin Vane   Args.push_back("placeholder.cpp");
238eb56f4feSEdwin Vane 
2393a4fe369SArtyom Skrobov   // Remove -no-integrated-as; it's not used for syntax checking,
2403a4fe369SArtyom Skrobov   // and it confuses targets which don't support this option.
2418562ec0cSNAKAMURA Takumi   std::remove_if(Args.begin(), Args.end(),
2428562ec0cSNAKAMURA Takumi                  MatchesAny(std::string("-no-integrated-as")));
2433a4fe369SArtyom Skrobov 
244b8984329SAhmed Charles   const std::unique_ptr<driver::Compilation> Compilation(
245eb56f4feSEdwin Vane       NewDriver->BuildCompilation(Args));
246eb56f4feSEdwin Vane 
247eb56f4feSEdwin Vane   const driver::JobList &Jobs = Compilation->getJobs();
248eb56f4feSEdwin Vane 
249eb56f4feSEdwin Vane   CompileJobAnalyzer CompileAnalyzer;
250eb56f4feSEdwin Vane 
251eb56f4feSEdwin Vane   for (driver::JobList::const_iterator I = Jobs.begin(), E = Jobs.end(); I != E;
252eb56f4feSEdwin Vane        ++I) {
253eb56f4feSEdwin Vane     if ((*I)->getKind() == driver::Job::CommandClass) {
254eb56f4feSEdwin Vane       const driver::Command *Cmd = cast<driver::Command>(*I);
255eb56f4feSEdwin Vane       // Collect only for Assemble jobs. If we do all jobs we get duplicates
256eb56f4feSEdwin Vane       // since Link jobs point to Assemble jobs as inputs.
257eb56f4feSEdwin Vane       if (Cmd->getSource().getKind() == driver::Action::AssembleJobClass)
258eb56f4feSEdwin Vane         CompileAnalyzer.run(&Cmd->getSource());
259eb56f4feSEdwin Vane     }
260eb56f4feSEdwin Vane   }
261eb56f4feSEdwin Vane 
262eb56f4feSEdwin Vane   if (CompileAnalyzer.Inputs.empty()) {
263eb56f4feSEdwin Vane     // No compile jobs found.
264eb56f4feSEdwin Vane     // FIXME: Emit a warning of some kind?
265eb56f4feSEdwin Vane     return false;
266eb56f4feSEdwin Vane   }
267eb56f4feSEdwin Vane 
268eb56f4feSEdwin Vane   // Remove all compilation input files from the command line. This is
269eb56f4feSEdwin Vane   // necessary so that getCompileCommands() can construct a command line for
270eb56f4feSEdwin Vane   // each file.
271eb56f4feSEdwin Vane   std::vector<const char *>::iterator End = std::remove_if(
272eb56f4feSEdwin Vane       Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
273eb56f4feSEdwin Vane 
274eb56f4feSEdwin Vane   // Remove all inputs deemed unused for compilation.
275eb56f4feSEdwin Vane   End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
276eb56f4feSEdwin Vane 
277eb56f4feSEdwin Vane   // Remove the -c add above as well. It will be at the end right now.
278e99bb4b2SRichard Trieu   assert(strcmp(*(End - 1), "-c") == 0);
279eb56f4feSEdwin Vane   --End;
280eb56f4feSEdwin Vane 
281eb56f4feSEdwin Vane   Result = std::vector<std::string>(Args.begin() + 1, End);
282eb56f4feSEdwin Vane   return true;
283eb56f4feSEdwin Vane }
284eb56f4feSEdwin Vane 
285ff26efceSManuel Klimek FixedCompilationDatabase *
286ff26efceSManuel Klimek FixedCompilationDatabase::loadFromCommandLine(int &Argc,
287ff26efceSManuel Klimek                                               const char **Argv,
288ff26efceSManuel Klimek                                               Twine Directory) {
289ff26efceSManuel Klimek   const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
290ff26efceSManuel Klimek   if (DoubleDash == Argv + Argc)
291ff26efceSManuel Klimek     return NULL;
292eb56f4feSEdwin Vane   std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
293ff26efceSManuel Klimek   Argc = DoubleDash - Argv;
294eb56f4feSEdwin Vane 
295eb56f4feSEdwin Vane   std::vector<std::string> StrippedArgs;
296eb56f4feSEdwin Vane   if (!stripPositionalArgs(CommandLine, StrippedArgs))
297eb56f4feSEdwin Vane     return 0;
298eb56f4feSEdwin Vane   return new FixedCompilationDatabase(Directory, StrippedArgs);
299ff26efceSManuel Klimek }
300ff26efceSManuel Klimek 
301ff26efceSManuel Klimek FixedCompilationDatabase::
302ff26efceSManuel Klimek FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
303ff26efceSManuel Klimek   std::vector<std::string> ToolCommandLine(1, "clang-tool");
304ff26efceSManuel Klimek   ToolCommandLine.insert(ToolCommandLine.end(),
305ff26efceSManuel Klimek                          CommandLine.begin(), CommandLine.end());
306*3f755aa7SBenjamin Kramer   CompileCommands.push_back(
307*3f755aa7SBenjamin Kramer       CompileCommand(Directory, std::move(ToolCommandLine)));
308ff26efceSManuel Klimek }
309ff26efceSManuel Klimek 
310ff26efceSManuel Klimek std::vector<CompileCommand>
311ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
312ff26efceSManuel Klimek   std::vector<CompileCommand> Result(CompileCommands);
313ff26efceSManuel Klimek   Result[0].CommandLine.push_back(FilePath);
314ff26efceSManuel Klimek   return Result;
315ff26efceSManuel Klimek }
316ff26efceSManuel Klimek 
31760b80161SManuel Klimek std::vector<std::string>
31860b80161SManuel Klimek FixedCompilationDatabase::getAllFiles() const {
31960b80161SManuel Klimek   return std::vector<std::string>();
32060b80161SManuel Klimek }
32160b80161SManuel Klimek 
322251ad5e0SArgyrios Kyrtzidis std::vector<CompileCommand>
323251ad5e0SArgyrios Kyrtzidis FixedCompilationDatabase::getAllCompileCommands() const {
324251ad5e0SArgyrios Kyrtzidis   return std::vector<CompileCommand>();
325251ad5e0SArgyrios Kyrtzidis }
326251ad5e0SArgyrios Kyrtzidis 
3276ed1f85cSDaniel Jasper // This anchor is used to force the linker to link in the generated object file
3286ed1f85cSDaniel Jasper // and thus register the JSONCompilationDatabasePlugin.
3296ed1f85cSDaniel Jasper extern volatile int JSONAnchorSource;
3306ed1f85cSDaniel Jasper static int JSONAnchorDest = JSONAnchorSource;
33147c245a5SManuel Klimek 
33247c245a5SManuel Klimek } // end namespace tooling
33347c245a5SManuel Klimek } // end namespace clang
334