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"
17f3ca2698SBenjamin Kramer #include "clang/Basic/DiagnosticOptions.h"
18eb56f4feSEdwin Vane #include "clang/Driver/Action.h"
195553d0d4SChandler Carruth #include "clang/Driver/Compilation.h"
20eb56f4feSEdwin Vane #include "clang/Driver/Driver.h"
21eb56f4feSEdwin Vane #include "clang/Driver/DriverDiagnostic.h"
22eb56f4feSEdwin Vane #include "clang/Driver/Job.h"
23eb56f4feSEdwin Vane #include "clang/Frontend/TextDiagnosticPrinter.h"
245553d0d4SChandler Carruth #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
255553d0d4SChandler Carruth #include "clang/Tooling/Tooling.h"
265553d0d4SChandler Carruth #include "llvm/ADT/SmallString.h"
27eb56f4feSEdwin Vane #include "llvm/Option/Arg.h"
285553d0d4SChandler Carruth #include "llvm/Support/Host.h"
295553d0d4SChandler Carruth #include "llvm/Support/Path.h"
305553d0d4SChandler Carruth #include <sstream>
318a8e554aSRafael Espindola #include <system_error>
326a1457e6SBenjamin Kramer using namespace clang;
336a1457e6SBenjamin Kramer using namespace tooling;
3447c245a5SManuel Klimek 
354d79ec7fSJohn Brawn LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
364d79ec7fSJohn Brawn 
37637d1e66SAngel Garcia Gomez CompilationDatabase::~CompilationDatabase() {}
3847c245a5SManuel Klimek 
39cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase>
4047c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
4147c245a5SManuel Klimek                                        std::string &ErrorMessage) {
42*2d487db0SMehdi Amini   llvm::raw_string_ostream ErrorStream(ErrorMessage);
436ed1f85cSDaniel Jasper   for (CompilationDatabasePluginRegistry::iterator
446ed1f85cSDaniel Jasper        It = CompilationDatabasePluginRegistry::begin(),
456ed1f85cSDaniel Jasper        Ie = CompilationDatabasePluginRegistry::end();
466ed1f85cSDaniel Jasper        It != Ie; ++It) {
476ed1f85cSDaniel Jasper     std::string DatabaseErrorMessage;
48b8984329SAhmed Charles     std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
49cdba84c0SDavid Blaikie     if (std::unique_ptr<CompilationDatabase> DB =
506ed1f85cSDaniel Jasper             Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
516ed1f85cSDaniel Jasper       return DB;
52*2d487db0SMehdi Amini     ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
5347c245a5SManuel Klimek   }
54ccbc35edSCraig Topper   return nullptr;
5547c245a5SManuel Klimek }
5647c245a5SManuel Klimek 
57cdba84c0SDavid Blaikie static std::unique_ptr<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 
65cdba84c0SDavid Blaikie     if (std::unique_ptr<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();
78ccbc35edSCraig Topper   return nullptr;
79617f5269SArnaud A. de Grandmaison }
80617f5269SArnaud A. de Grandmaison 
81cdba84c0SDavid Blaikie std::unique_ptr<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 
87cdba84c0SDavid Blaikie   std::unique_ptr<CompilationDatabase> DB =
88cdba84c0SDavid Blaikie       findCompilationDatabaseFromDirectory(Directory, 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 
96cdba84c0SDavid Blaikie std::unique_ptr<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 
101cdba84c0SDavid Blaikie   std::unique_ptr<CompilationDatabase> DB =
102cdba84c0SDavid Blaikie       findCompilationDatabaseFromDirectory(AbsolutePath, 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 
110637d1e66SAngel Garcia Gomez CompilationDatabasePlugin::~CompilationDatabasePlugin() {}
1116ed1f85cSDaniel Jasper 
1126a1457e6SBenjamin Kramer namespace {
113eb56f4feSEdwin Vane // Helper for recursively searching through a chain of actions and collecting
114eb56f4feSEdwin Vane // all inputs, direct and indirect, of compile jobs.
115eb56f4feSEdwin Vane struct CompileJobAnalyzer {
116eb56f4feSEdwin Vane   void run(const driver::Action *A) {
117eb56f4feSEdwin Vane     runImpl(A, false);
118eb56f4feSEdwin Vane   }
119eb56f4feSEdwin Vane 
120eb56f4feSEdwin Vane   SmallVector<std::string, 2> Inputs;
121eb56f4feSEdwin Vane 
122eb56f4feSEdwin Vane private:
123eb56f4feSEdwin Vane 
124eb56f4feSEdwin Vane   void runImpl(const driver::Action *A, bool Collect) {
125eb56f4feSEdwin Vane     bool CollectChildren = Collect;
126eb56f4feSEdwin Vane     switch (A->getKind()) {
127eb56f4feSEdwin Vane     case driver::Action::CompileJobClass:
128eb56f4feSEdwin Vane       CollectChildren = true;
129eb56f4feSEdwin Vane       break;
130eb56f4feSEdwin Vane 
131eb56f4feSEdwin Vane     case driver::Action::InputClass: {
132eb56f4feSEdwin Vane       if (Collect) {
133eb56f4feSEdwin Vane         const driver::InputAction *IA = cast<driver::InputAction>(A);
134eb56f4feSEdwin Vane         Inputs.push_back(IA->getInputArg().getSpelling());
135eb56f4feSEdwin Vane       }
136eb56f4feSEdwin Vane     } break;
137eb56f4feSEdwin Vane 
138eb56f4feSEdwin Vane     default:
139eb56f4feSEdwin Vane       // Don't care about others
140eb56f4feSEdwin Vane       ;
141eb56f4feSEdwin Vane     }
142eb56f4feSEdwin Vane 
1435a459f82SNico Weber     for (const driver::Action *AI : A->inputs())
1445a459f82SNico Weber       runImpl(AI, 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:
153ccbc35edSCraig Topper   UnusedInputDiagConsumer() : Other(nullptr) {}
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 
15934eb2072SAlexander Kornienko   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 };
1866a1457e6SBenjamin Kramer } // namespace
187eb56f4feSEdwin Vane 
188eb56f4feSEdwin Vane /// \brief Strips any positional args and possible argv[0] from a command-line
189eb56f4feSEdwin Vane /// provided by the user to construct a FixedCompilationDatabase.
190eb56f4feSEdwin Vane ///
191eb56f4feSEdwin Vane /// FixedCompilationDatabase requires a command line to be in this format as it
192eb56f4feSEdwin Vane /// constructs the command line for each file by appending the name of the file
193eb56f4feSEdwin Vane /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
194eb56f4feSEdwin Vane /// start of the command line although its value is not important as it's just
195eb56f4feSEdwin Vane /// ignored by the Driver invoked by the ClangTool using the
196eb56f4feSEdwin Vane /// FixedCompilationDatabase.
197eb56f4feSEdwin Vane ///
198eb56f4feSEdwin Vane /// FIXME: This functionality should probably be made available by
199eb56f4feSEdwin Vane /// clang::driver::Driver although what the interface should look like is not
200eb56f4feSEdwin Vane /// clear.
201eb56f4feSEdwin Vane ///
202eb56f4feSEdwin Vane /// \param[in] Args Args as provided by the user.
2035335cf33SNAKAMURA Takumi /// \return Resulting stripped command line.
2045335cf33SNAKAMURA Takumi ///          \li true if successful.
205eb56f4feSEdwin Vane ///          \li false if \c Args cannot be used for compilation jobs (e.g.
206eb56f4feSEdwin Vane ///          contains an option like -E or -version).
2073a4fe369SArtyom Skrobov static bool stripPositionalArgs(std::vector<const char *> Args,
208eb56f4feSEdwin Vane                                 std::vector<std::string> &Result) {
209eb56f4feSEdwin Vane   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
210eb56f4feSEdwin Vane   UnusedInputDiagConsumer DiagClient;
211eb56f4feSEdwin Vane   DiagnosticsEngine Diagnostics(
212eb56f4feSEdwin Vane       IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()),
213eb56f4feSEdwin Vane       &*DiagOpts, &DiagClient, false);
214eb56f4feSEdwin Vane 
2151761f118SAlp Toker   // The clang executable path isn't required since the jobs the driver builds
2161761f118SAlp Toker   // will not be executed.
217b8984329SAhmed Charles   std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
218eb56f4feSEdwin Vane       /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
2191761f118SAlp Toker       Diagnostics));
220eb56f4feSEdwin Vane   NewDriver->setCheckInputsExist(false);
221eb56f4feSEdwin Vane 
222eb56f4feSEdwin Vane   // This becomes the new argv[0]. The value is actually not important as it
223eb56f4feSEdwin Vane   // isn't used for invoking Tools.
224eb56f4feSEdwin Vane   Args.insert(Args.begin(), "clang-tool");
225eb56f4feSEdwin Vane 
226eb56f4feSEdwin Vane   // By adding -c, we force the driver to treat compilation as the last phase.
227eb56f4feSEdwin Vane   // It will then issue warnings via Diagnostics about un-used options that
228eb56f4feSEdwin Vane   // would have been used for linking. If the user provided a compiler name as
229eb56f4feSEdwin Vane   // the original argv[0], this will be treated as a linker input thanks to
230eb56f4feSEdwin Vane   // insertng a new argv[0] above. All un-used options get collected by
231eb56f4feSEdwin Vane   // UnusedInputdiagConsumer and get stripped out later.
232eb56f4feSEdwin Vane   Args.push_back("-c");
233eb56f4feSEdwin Vane 
234eb56f4feSEdwin Vane   // Put a dummy C++ file on to ensure there's at least one compile job for the
235eb56f4feSEdwin Vane   // driver to construct. If the user specified some other argument that
236eb56f4feSEdwin Vane   // prevents compilation, e.g. -E or something like -version, we may still end
237eb56f4feSEdwin Vane   // up with no jobs but then this is the user's fault.
238eb56f4feSEdwin Vane   Args.push_back("placeholder.cpp");
239eb56f4feSEdwin Vane 
2403a4fe369SArtyom Skrobov   // Remove -no-integrated-as; it's not used for syntax checking,
2413a4fe369SArtyom Skrobov   // and it confuses targets which don't support this option.
242e1265143SArnaud A. de Grandmaison   Args.erase(std::remove_if(Args.begin(), Args.end(),
243e1265143SArnaud A. de Grandmaison                             MatchesAny(std::string("-no-integrated-as"))),
244e1265143SArnaud A. de Grandmaison              Args.end());
2453a4fe369SArtyom Skrobov 
246b8984329SAhmed Charles   const std::unique_ptr<driver::Compilation> Compilation(
247eb56f4feSEdwin Vane       NewDriver->BuildCompilation(Args));
248eb56f4feSEdwin Vane 
249eb56f4feSEdwin Vane   const driver::JobList &Jobs = Compilation->getJobs();
250eb56f4feSEdwin Vane 
251eb56f4feSEdwin Vane   CompileJobAnalyzer CompileAnalyzer;
252eb56f4feSEdwin Vane 
2530cd9248dSJustin Bogner   for (const auto &Cmd : Jobs) {
254eb56f4feSEdwin Vane     // Collect only for Assemble jobs. If we do all jobs we get duplicates
255eb56f4feSEdwin Vane     // since Link jobs point to Assemble jobs as inputs.
256c11bf802SDavid Blaikie     if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass)
257c11bf802SDavid Blaikie       CompileAnalyzer.run(&Cmd.getSource());
258eb56f4feSEdwin Vane   }
259eb56f4feSEdwin Vane 
260eb56f4feSEdwin Vane   if (CompileAnalyzer.Inputs.empty()) {
261eb56f4feSEdwin Vane     // No compile jobs found.
262eb56f4feSEdwin Vane     // FIXME: Emit a warning of some kind?
263eb56f4feSEdwin Vane     return false;
264eb56f4feSEdwin Vane   }
265eb56f4feSEdwin Vane 
266eb56f4feSEdwin Vane   // Remove all compilation input files from the command line. This is
267eb56f4feSEdwin Vane   // necessary so that getCompileCommands() can construct a command line for
268eb56f4feSEdwin Vane   // each file.
269eb56f4feSEdwin Vane   std::vector<const char *>::iterator End = std::remove_if(
270eb56f4feSEdwin Vane       Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
271eb56f4feSEdwin Vane 
272eb56f4feSEdwin Vane   // Remove all inputs deemed unused for compilation.
273eb56f4feSEdwin Vane   End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
274eb56f4feSEdwin Vane 
275eb56f4feSEdwin Vane   // Remove the -c add above as well. It will be at the end right now.
276e99bb4b2SRichard Trieu   assert(strcmp(*(End - 1), "-c") == 0);
277eb56f4feSEdwin Vane   --End;
278eb56f4feSEdwin Vane 
279eb56f4feSEdwin Vane   Result = std::vector<std::string>(Args.begin() + 1, End);
280eb56f4feSEdwin Vane   return true;
281eb56f4feSEdwin Vane }
282eb56f4feSEdwin Vane 
283ab8f7d58SDavid Blaikie FixedCompilationDatabase *FixedCompilationDatabase::loadFromCommandLine(
284ab8f7d58SDavid Blaikie     int &Argc, const char *const *Argv, Twine Directory) {
285ab8f7d58SDavid Blaikie   const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
286ff26efceSManuel Klimek   if (DoubleDash == Argv + Argc)
287ccbc35edSCraig Topper     return nullptr;
288eb56f4feSEdwin Vane   std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
289ff26efceSManuel Klimek   Argc = DoubleDash - Argv;
290eb56f4feSEdwin Vane 
291eb56f4feSEdwin Vane   std::vector<std::string> StrippedArgs;
292eb56f4feSEdwin Vane   if (!stripPositionalArgs(CommandLine, StrippedArgs))
293ccbc35edSCraig Topper     return nullptr;
294eb56f4feSEdwin Vane   return new FixedCompilationDatabase(Directory, StrippedArgs);
295ff26efceSManuel Klimek }
296ff26efceSManuel Klimek 
297ff26efceSManuel Klimek FixedCompilationDatabase::
298ff26efceSManuel Klimek FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
299ff26efceSManuel Klimek   std::vector<std::string> ToolCommandLine(1, "clang-tool");
300ff26efceSManuel Klimek   ToolCommandLine.insert(ToolCommandLine.end(),
301ff26efceSManuel Klimek                          CommandLine.begin(), CommandLine.end());
30274bcd21eSArgyrios Kyrtzidis   CompileCommands.emplace_back(Directory, StringRef(),
30374bcd21eSArgyrios Kyrtzidis                                std::move(ToolCommandLine));
304ff26efceSManuel Klimek }
305ff26efceSManuel Klimek 
306ff26efceSManuel Klimek std::vector<CompileCommand>
307ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
308ff26efceSManuel Klimek   std::vector<CompileCommand> Result(CompileCommands);
309ff26efceSManuel Klimek   Result[0].CommandLine.push_back(FilePath);
31074bcd21eSArgyrios Kyrtzidis   Result[0].Filename = FilePath;
311ff26efceSManuel Klimek   return Result;
312ff26efceSManuel Klimek }
313ff26efceSManuel Klimek 
31460b80161SManuel Klimek std::vector<std::string>
31560b80161SManuel Klimek FixedCompilationDatabase::getAllFiles() const {
31660b80161SManuel Klimek   return std::vector<std::string>();
31760b80161SManuel Klimek }
31860b80161SManuel Klimek 
319251ad5e0SArgyrios Kyrtzidis std::vector<CompileCommand>
320251ad5e0SArgyrios Kyrtzidis FixedCompilationDatabase::getAllCompileCommands() const {
321251ad5e0SArgyrios Kyrtzidis   return std::vector<CompileCommand>();
322251ad5e0SArgyrios Kyrtzidis }
323251ad5e0SArgyrios Kyrtzidis 
3246a1457e6SBenjamin Kramer namespace clang {
3256a1457e6SBenjamin Kramer namespace tooling {
3266a1457e6SBenjamin Kramer 
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;
330b7e8c7c7SYaron Keren static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
33147c245a5SManuel Klimek 
33247c245a5SManuel Klimek } // end namespace tooling
33347c245a5SManuel Klimek } // end namespace clang
334