xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/JSONCompilationDatabase.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===- JSONCompilationDatabase.cpp ----------------------------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //  This file contains the implementation of the JSONCompilationDatabase.
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12*0b57cec5SDimitry Andric 
13*0b57cec5SDimitry Andric #include "clang/Tooling/JSONCompilationDatabase.h"
14*0b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
15*0b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabase.h"
16*0b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
17*0b57cec5SDimitry Andric #include "clang/Tooling/Tooling.h"
18*0b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
19*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
20*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
21*0b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
22*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
23*0b57cec5SDimitry Andric #include "llvm/ADT/Triple.h"
24*0b57cec5SDimitry Andric #include "llvm/Support/Allocator.h"
25*0b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
26*0b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
27*0b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
28*0b57cec5SDimitry Andric #include "llvm/Support/Host.h"
29*0b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
30*0b57cec5SDimitry Andric #include "llvm/Support/Path.h"
31*0b57cec5SDimitry Andric #include "llvm/Support/StringSaver.h"
32*0b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h"
33*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
34*0b57cec5SDimitry Andric #include <cassert>
35*0b57cec5SDimitry Andric #include <memory>
36*0b57cec5SDimitry Andric #include <string>
37*0b57cec5SDimitry Andric #include <system_error>
38*0b57cec5SDimitry Andric #include <tuple>
39*0b57cec5SDimitry Andric #include <utility>
40*0b57cec5SDimitry Andric #include <vector>
41*0b57cec5SDimitry Andric 
42*0b57cec5SDimitry Andric using namespace clang;
43*0b57cec5SDimitry Andric using namespace tooling;
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric namespace {
46*0b57cec5SDimitry Andric 
47*0b57cec5SDimitry Andric /// A parser for escaped strings of command line arguments.
48*0b57cec5SDimitry Andric ///
49*0b57cec5SDimitry Andric /// Assumes \-escaping for quoted arguments (see the documentation of
50*0b57cec5SDimitry Andric /// unescapeCommandLine(...)).
51*0b57cec5SDimitry Andric class CommandLineArgumentParser {
52*0b57cec5SDimitry Andric  public:
53*0b57cec5SDimitry Andric   CommandLineArgumentParser(StringRef CommandLine)
54*0b57cec5SDimitry Andric       : Input(CommandLine), Position(Input.begin()-1) {}
55*0b57cec5SDimitry Andric 
56*0b57cec5SDimitry Andric   std::vector<std::string> parse() {
57*0b57cec5SDimitry Andric     bool HasMoreInput = true;
58*0b57cec5SDimitry Andric     while (HasMoreInput && nextNonWhitespace()) {
59*0b57cec5SDimitry Andric       std::string Argument;
60*0b57cec5SDimitry Andric       HasMoreInput = parseStringInto(Argument);
61*0b57cec5SDimitry Andric       CommandLine.push_back(Argument);
62*0b57cec5SDimitry Andric     }
63*0b57cec5SDimitry Andric     return CommandLine;
64*0b57cec5SDimitry Andric   }
65*0b57cec5SDimitry Andric 
66*0b57cec5SDimitry Andric  private:
67*0b57cec5SDimitry Andric   // All private methods return true if there is more input available.
68*0b57cec5SDimitry Andric 
69*0b57cec5SDimitry Andric   bool parseStringInto(std::string &String) {
70*0b57cec5SDimitry Andric     do {
71*0b57cec5SDimitry Andric       if (*Position == '"') {
72*0b57cec5SDimitry Andric         if (!parseDoubleQuotedStringInto(String)) return false;
73*0b57cec5SDimitry Andric       } else if (*Position == '\'') {
74*0b57cec5SDimitry Andric         if (!parseSingleQuotedStringInto(String)) return false;
75*0b57cec5SDimitry Andric       } else {
76*0b57cec5SDimitry Andric         if (!parseFreeStringInto(String)) return false;
77*0b57cec5SDimitry Andric       }
78*0b57cec5SDimitry Andric     } while (*Position != ' ');
79*0b57cec5SDimitry Andric     return true;
80*0b57cec5SDimitry Andric   }
81*0b57cec5SDimitry Andric 
82*0b57cec5SDimitry Andric   bool parseDoubleQuotedStringInto(std::string &String) {
83*0b57cec5SDimitry Andric     if (!next()) return false;
84*0b57cec5SDimitry Andric     while (*Position != '"') {
85*0b57cec5SDimitry Andric       if (!skipEscapeCharacter()) return false;
86*0b57cec5SDimitry Andric       String.push_back(*Position);
87*0b57cec5SDimitry Andric       if (!next()) return false;
88*0b57cec5SDimitry Andric     }
89*0b57cec5SDimitry Andric     return next();
90*0b57cec5SDimitry Andric   }
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric   bool parseSingleQuotedStringInto(std::string &String) {
93*0b57cec5SDimitry Andric     if (!next()) return false;
94*0b57cec5SDimitry Andric     while (*Position != '\'') {
95*0b57cec5SDimitry Andric       String.push_back(*Position);
96*0b57cec5SDimitry Andric       if (!next()) return false;
97*0b57cec5SDimitry Andric     }
98*0b57cec5SDimitry Andric     return next();
99*0b57cec5SDimitry Andric   }
100*0b57cec5SDimitry Andric 
101*0b57cec5SDimitry Andric   bool parseFreeStringInto(std::string &String) {
102*0b57cec5SDimitry Andric     do {
103*0b57cec5SDimitry Andric       if (!skipEscapeCharacter()) return false;
104*0b57cec5SDimitry Andric       String.push_back(*Position);
105*0b57cec5SDimitry Andric       if (!next()) return false;
106*0b57cec5SDimitry Andric     } while (*Position != ' ' && *Position != '"' && *Position != '\'');
107*0b57cec5SDimitry Andric     return true;
108*0b57cec5SDimitry Andric   }
109*0b57cec5SDimitry Andric 
110*0b57cec5SDimitry Andric   bool skipEscapeCharacter() {
111*0b57cec5SDimitry Andric     if (*Position == '\\') {
112*0b57cec5SDimitry Andric       return next();
113*0b57cec5SDimitry Andric     }
114*0b57cec5SDimitry Andric     return true;
115*0b57cec5SDimitry Andric   }
116*0b57cec5SDimitry Andric 
117*0b57cec5SDimitry Andric   bool nextNonWhitespace() {
118*0b57cec5SDimitry Andric     do {
119*0b57cec5SDimitry Andric       if (!next()) return false;
120*0b57cec5SDimitry Andric     } while (*Position == ' ');
121*0b57cec5SDimitry Andric     return true;
122*0b57cec5SDimitry Andric   }
123*0b57cec5SDimitry Andric 
124*0b57cec5SDimitry Andric   bool next() {
125*0b57cec5SDimitry Andric     ++Position;
126*0b57cec5SDimitry Andric     return Position != Input.end();
127*0b57cec5SDimitry Andric   }
128*0b57cec5SDimitry Andric 
129*0b57cec5SDimitry Andric   const StringRef Input;
130*0b57cec5SDimitry Andric   StringRef::iterator Position;
131*0b57cec5SDimitry Andric   std::vector<std::string> CommandLine;
132*0b57cec5SDimitry Andric };
133*0b57cec5SDimitry Andric 
134*0b57cec5SDimitry Andric std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax,
135*0b57cec5SDimitry Andric                                              StringRef EscapedCommandLine) {
136*0b57cec5SDimitry Andric   if (Syntax == JSONCommandLineSyntax::AutoDetect) {
137*0b57cec5SDimitry Andric     Syntax = JSONCommandLineSyntax::Gnu;
138*0b57cec5SDimitry Andric     llvm::Triple Triple(llvm::sys::getProcessTriple());
139*0b57cec5SDimitry Andric     if (Triple.getOS() == llvm::Triple::OSType::Win32) {
140*0b57cec5SDimitry Andric       // Assume Windows command line parsing on Win32 unless the triple
141*0b57cec5SDimitry Andric       // explicitly tells us otherwise.
142*0b57cec5SDimitry Andric       if (!Triple.hasEnvironment() ||
143*0b57cec5SDimitry Andric           Triple.getEnvironment() == llvm::Triple::EnvironmentType::MSVC)
144*0b57cec5SDimitry Andric         Syntax = JSONCommandLineSyntax::Windows;
145*0b57cec5SDimitry Andric     }
146*0b57cec5SDimitry Andric   }
147*0b57cec5SDimitry Andric 
148*0b57cec5SDimitry Andric   if (Syntax == JSONCommandLineSyntax::Windows) {
149*0b57cec5SDimitry Andric     llvm::BumpPtrAllocator Alloc;
150*0b57cec5SDimitry Andric     llvm::StringSaver Saver(Alloc);
151*0b57cec5SDimitry Andric     llvm::SmallVector<const char *, 64> T;
152*0b57cec5SDimitry Andric     llvm::cl::TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T);
153*0b57cec5SDimitry Andric     std::vector<std::string> Result(T.begin(), T.end());
154*0b57cec5SDimitry Andric     return Result;
155*0b57cec5SDimitry Andric   }
156*0b57cec5SDimitry Andric   assert(Syntax == JSONCommandLineSyntax::Gnu);
157*0b57cec5SDimitry Andric   CommandLineArgumentParser parser(EscapedCommandLine);
158*0b57cec5SDimitry Andric   return parser.parse();
159*0b57cec5SDimitry Andric }
160*0b57cec5SDimitry Andric 
161*0b57cec5SDimitry Andric // This plugin locates a nearby compile_command.json file, and also infers
162*0b57cec5SDimitry Andric // compile commands for files not present in the database.
163*0b57cec5SDimitry Andric class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
164*0b57cec5SDimitry Andric   std::unique_ptr<CompilationDatabase>
165*0b57cec5SDimitry Andric   loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
166*0b57cec5SDimitry Andric     SmallString<1024> JSONDatabasePath(Directory);
167*0b57cec5SDimitry Andric     llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
168*0b57cec5SDimitry Andric     auto Base = JSONCompilationDatabase::loadFromFile(
169*0b57cec5SDimitry Andric         JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect);
170*0b57cec5SDimitry Andric     return Base ? inferTargetAndDriverMode(
171*0b57cec5SDimitry Andric                       inferMissingCompileCommands(std::move(Base)))
172*0b57cec5SDimitry Andric                 : nullptr;
173*0b57cec5SDimitry Andric   }
174*0b57cec5SDimitry Andric };
175*0b57cec5SDimitry Andric 
176*0b57cec5SDimitry Andric } // namespace
177*0b57cec5SDimitry Andric 
178*0b57cec5SDimitry Andric // Register the JSONCompilationDatabasePlugin with the
179*0b57cec5SDimitry Andric // CompilationDatabasePluginRegistry using this statically initialized variable.
180*0b57cec5SDimitry Andric static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
181*0b57cec5SDimitry Andric X("json-compilation-database", "Reads JSON formatted compilation databases");
182*0b57cec5SDimitry Andric 
183*0b57cec5SDimitry Andric namespace clang {
184*0b57cec5SDimitry Andric namespace tooling {
185*0b57cec5SDimitry Andric 
186*0b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file
187*0b57cec5SDimitry Andric // and thus register the JSONCompilationDatabasePlugin.
188*0b57cec5SDimitry Andric volatile int JSONAnchorSource = 0;
189*0b57cec5SDimitry Andric 
190*0b57cec5SDimitry Andric } // namespace tooling
191*0b57cec5SDimitry Andric } // namespace clang
192*0b57cec5SDimitry Andric 
193*0b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase>
194*0b57cec5SDimitry Andric JSONCompilationDatabase::loadFromFile(StringRef FilePath,
195*0b57cec5SDimitry Andric                                       std::string &ErrorMessage,
196*0b57cec5SDimitry Andric                                       JSONCommandLineSyntax Syntax) {
197*0b57cec5SDimitry Andric   // Don't mmap: if we're a long-lived process, the build system may overwrite.
198*0b57cec5SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
199*0b57cec5SDimitry Andric       llvm::MemoryBuffer::getFile(FilePath, /*FileSize=*/-1,
200*0b57cec5SDimitry Andric                                   /*RequiresNullTerminator=*/true,
201*0b57cec5SDimitry Andric                                   /*IsVolatile=*/true);
202*0b57cec5SDimitry Andric   if (std::error_code Result = DatabaseBuffer.getError()) {
203*0b57cec5SDimitry Andric     ErrorMessage = "Error while opening JSON database: " + Result.message();
204*0b57cec5SDimitry Andric     return nullptr;
205*0b57cec5SDimitry Andric   }
206*0b57cec5SDimitry Andric   std::unique_ptr<JSONCompilationDatabase> Database(
207*0b57cec5SDimitry Andric       new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax));
208*0b57cec5SDimitry Andric   if (!Database->parse(ErrorMessage))
209*0b57cec5SDimitry Andric     return nullptr;
210*0b57cec5SDimitry Andric   return Database;
211*0b57cec5SDimitry Andric }
212*0b57cec5SDimitry Andric 
213*0b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase>
214*0b57cec5SDimitry Andric JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
215*0b57cec5SDimitry Andric                                         std::string &ErrorMessage,
216*0b57cec5SDimitry Andric                                         JSONCommandLineSyntax Syntax) {
217*0b57cec5SDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
218*0b57cec5SDimitry Andric       llvm::MemoryBuffer::getMemBuffer(DatabaseString));
219*0b57cec5SDimitry Andric   std::unique_ptr<JSONCompilationDatabase> Database(
220*0b57cec5SDimitry Andric       new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax));
221*0b57cec5SDimitry Andric   if (!Database->parse(ErrorMessage))
222*0b57cec5SDimitry Andric     return nullptr;
223*0b57cec5SDimitry Andric   return Database;
224*0b57cec5SDimitry Andric }
225*0b57cec5SDimitry Andric 
226*0b57cec5SDimitry Andric std::vector<CompileCommand>
227*0b57cec5SDimitry Andric JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
228*0b57cec5SDimitry Andric   SmallString<128> NativeFilePath;
229*0b57cec5SDimitry Andric   llvm::sys::path::native(FilePath, NativeFilePath);
230*0b57cec5SDimitry Andric 
231*0b57cec5SDimitry Andric   std::string Error;
232*0b57cec5SDimitry Andric   llvm::raw_string_ostream ES(Error);
233*0b57cec5SDimitry Andric   StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
234*0b57cec5SDimitry Andric   if (Match.empty())
235*0b57cec5SDimitry Andric     return {};
236*0b57cec5SDimitry Andric   const auto CommandsRefI = IndexByFile.find(Match);
237*0b57cec5SDimitry Andric   if (CommandsRefI == IndexByFile.end())
238*0b57cec5SDimitry Andric     return {};
239*0b57cec5SDimitry Andric   std::vector<CompileCommand> Commands;
240*0b57cec5SDimitry Andric   getCommands(CommandsRefI->getValue(), Commands);
241*0b57cec5SDimitry Andric   return Commands;
242*0b57cec5SDimitry Andric }
243*0b57cec5SDimitry Andric 
244*0b57cec5SDimitry Andric std::vector<std::string>
245*0b57cec5SDimitry Andric JSONCompilationDatabase::getAllFiles() const {
246*0b57cec5SDimitry Andric   std::vector<std::string> Result;
247*0b57cec5SDimitry Andric   for (const auto &CommandRef : IndexByFile)
248*0b57cec5SDimitry Andric     Result.push_back(CommandRef.first().str());
249*0b57cec5SDimitry Andric   return Result;
250*0b57cec5SDimitry Andric }
251*0b57cec5SDimitry Andric 
252*0b57cec5SDimitry Andric std::vector<CompileCommand>
253*0b57cec5SDimitry Andric JSONCompilationDatabase::getAllCompileCommands() const {
254*0b57cec5SDimitry Andric   std::vector<CompileCommand> Commands;
255*0b57cec5SDimitry Andric   getCommands(AllCommands, Commands);
256*0b57cec5SDimitry Andric   return Commands;
257*0b57cec5SDimitry Andric }
258*0b57cec5SDimitry Andric 
259*0b57cec5SDimitry Andric static llvm::StringRef stripExecutableExtension(llvm::StringRef Name) {
260*0b57cec5SDimitry Andric   Name.consume_back(".exe");
261*0b57cec5SDimitry Andric   return Name;
262*0b57cec5SDimitry Andric }
263*0b57cec5SDimitry Andric 
264*0b57cec5SDimitry Andric // There are compiler-wrappers (ccache, distcc, gomacc) that take the "real"
265*0b57cec5SDimitry Andric // compiler as an argument, e.g. distcc gcc -O3 foo.c.
266*0b57cec5SDimitry Andric // These end up in compile_commands.json when people set CC="distcc gcc".
267*0b57cec5SDimitry Andric // Clang's driver doesn't understand this, so we need to unwrap.
268*0b57cec5SDimitry Andric static bool unwrapCommand(std::vector<std::string> &Args) {
269*0b57cec5SDimitry Andric   if (Args.size() < 2)
270*0b57cec5SDimitry Andric     return false;
271*0b57cec5SDimitry Andric   StringRef Wrapper =
272*0b57cec5SDimitry Andric       stripExecutableExtension(llvm::sys::path::filename(Args.front()));
273*0b57cec5SDimitry Andric   if (Wrapper == "distcc" || Wrapper == "gomacc" || Wrapper == "ccache") {
274*0b57cec5SDimitry Andric     // Most of these wrappers support being invoked 3 ways:
275*0b57cec5SDimitry Andric     // `distcc g++ file.c` This is the mode we're trying to match.
276*0b57cec5SDimitry Andric     //                     We need to drop `distcc`.
277*0b57cec5SDimitry Andric     // `distcc file.c`     This acts like compiler is cc or similar.
278*0b57cec5SDimitry Andric     //                     Clang's driver can handle this, no change needed.
279*0b57cec5SDimitry Andric     // `g++ file.c`        g++ is a symlink to distcc.
280*0b57cec5SDimitry Andric     //                     We don't even notice this case, and all is well.
281*0b57cec5SDimitry Andric     //
282*0b57cec5SDimitry Andric     // We need to distinguish between the first and second case.
283*0b57cec5SDimitry Andric     // The wrappers themselves don't take flags, so Args[1] is a compiler flag,
284*0b57cec5SDimitry Andric     // an input file, or a compiler. Inputs have extensions, compilers don't.
285*0b57cec5SDimitry Andric     bool HasCompiler =
286*0b57cec5SDimitry Andric         (Args[1][0] != '-') &&
287*0b57cec5SDimitry Andric         !llvm::sys::path::has_extension(stripExecutableExtension(Args[1]));
288*0b57cec5SDimitry Andric     if (HasCompiler) {
289*0b57cec5SDimitry Andric       Args.erase(Args.begin());
290*0b57cec5SDimitry Andric       return true;
291*0b57cec5SDimitry Andric     }
292*0b57cec5SDimitry Andric     // If !HasCompiler, wrappers act like GCC. Fine: so do we.
293*0b57cec5SDimitry Andric   }
294*0b57cec5SDimitry Andric   return false;
295*0b57cec5SDimitry Andric }
296*0b57cec5SDimitry Andric 
297*0b57cec5SDimitry Andric static std::vector<std::string>
298*0b57cec5SDimitry Andric nodeToCommandLine(JSONCommandLineSyntax Syntax,
299*0b57cec5SDimitry Andric                   const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
300*0b57cec5SDimitry Andric   SmallString<1024> Storage;
301*0b57cec5SDimitry Andric   std::vector<std::string> Arguments;
302*0b57cec5SDimitry Andric   if (Nodes.size() == 1)
303*0b57cec5SDimitry Andric     Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
304*0b57cec5SDimitry Andric   else
305*0b57cec5SDimitry Andric     for (const auto *Node : Nodes)
306*0b57cec5SDimitry Andric       Arguments.push_back(Node->getValue(Storage));
307*0b57cec5SDimitry Andric   // There may be multiple wrappers: using distcc and ccache together is common.
308*0b57cec5SDimitry Andric   while (unwrapCommand(Arguments))
309*0b57cec5SDimitry Andric     ;
310*0b57cec5SDimitry Andric   return Arguments;
311*0b57cec5SDimitry Andric }
312*0b57cec5SDimitry Andric 
313*0b57cec5SDimitry Andric void JSONCompilationDatabase::getCommands(
314*0b57cec5SDimitry Andric     ArrayRef<CompileCommandRef> CommandsRef,
315*0b57cec5SDimitry Andric     std::vector<CompileCommand> &Commands) const {
316*0b57cec5SDimitry Andric   for (const auto &CommandRef : CommandsRef) {
317*0b57cec5SDimitry Andric     SmallString<8> DirectoryStorage;
318*0b57cec5SDimitry Andric     SmallString<32> FilenameStorage;
319*0b57cec5SDimitry Andric     SmallString<32> OutputStorage;
320*0b57cec5SDimitry Andric     auto Output = std::get<3>(CommandRef);
321*0b57cec5SDimitry Andric     Commands.emplace_back(
322*0b57cec5SDimitry Andric         std::get<0>(CommandRef)->getValue(DirectoryStorage),
323*0b57cec5SDimitry Andric         std::get<1>(CommandRef)->getValue(FilenameStorage),
324*0b57cec5SDimitry Andric         nodeToCommandLine(Syntax, std::get<2>(CommandRef)),
325*0b57cec5SDimitry Andric         Output ? Output->getValue(OutputStorage) : "");
326*0b57cec5SDimitry Andric   }
327*0b57cec5SDimitry Andric }
328*0b57cec5SDimitry Andric 
329*0b57cec5SDimitry Andric bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
330*0b57cec5SDimitry Andric   llvm::yaml::document_iterator I = YAMLStream.begin();
331*0b57cec5SDimitry Andric   if (I == YAMLStream.end()) {
332*0b57cec5SDimitry Andric     ErrorMessage = "Error while parsing YAML.";
333*0b57cec5SDimitry Andric     return false;
334*0b57cec5SDimitry Andric   }
335*0b57cec5SDimitry Andric   llvm::yaml::Node *Root = I->getRoot();
336*0b57cec5SDimitry Andric   if (!Root) {
337*0b57cec5SDimitry Andric     ErrorMessage = "Error while parsing YAML.";
338*0b57cec5SDimitry Andric     return false;
339*0b57cec5SDimitry Andric   }
340*0b57cec5SDimitry Andric   auto *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
341*0b57cec5SDimitry Andric   if (!Array) {
342*0b57cec5SDimitry Andric     ErrorMessage = "Expected array.";
343*0b57cec5SDimitry Andric     return false;
344*0b57cec5SDimitry Andric   }
345*0b57cec5SDimitry Andric   for (auto &NextObject : *Array) {
346*0b57cec5SDimitry Andric     auto *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
347*0b57cec5SDimitry Andric     if (!Object) {
348*0b57cec5SDimitry Andric       ErrorMessage = "Expected object.";
349*0b57cec5SDimitry Andric       return false;
350*0b57cec5SDimitry Andric     }
351*0b57cec5SDimitry Andric     llvm::yaml::ScalarNode *Directory = nullptr;
352*0b57cec5SDimitry Andric     llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
353*0b57cec5SDimitry Andric     llvm::yaml::ScalarNode *File = nullptr;
354*0b57cec5SDimitry Andric     llvm::yaml::ScalarNode *Output = nullptr;
355*0b57cec5SDimitry Andric     for (auto& NextKeyValue : *Object) {
356*0b57cec5SDimitry Andric       auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
357*0b57cec5SDimitry Andric       if (!KeyString) {
358*0b57cec5SDimitry Andric         ErrorMessage = "Expected strings as key.";
359*0b57cec5SDimitry Andric         return false;
360*0b57cec5SDimitry Andric       }
361*0b57cec5SDimitry Andric       SmallString<10> KeyStorage;
362*0b57cec5SDimitry Andric       StringRef KeyValue = KeyString->getValue(KeyStorage);
363*0b57cec5SDimitry Andric       llvm::yaml::Node *Value = NextKeyValue.getValue();
364*0b57cec5SDimitry Andric       if (!Value) {
365*0b57cec5SDimitry Andric         ErrorMessage = "Expected value.";
366*0b57cec5SDimitry Andric         return false;
367*0b57cec5SDimitry Andric       }
368*0b57cec5SDimitry Andric       auto *ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value);
369*0b57cec5SDimitry Andric       auto *SequenceString = dyn_cast<llvm::yaml::SequenceNode>(Value);
370*0b57cec5SDimitry Andric       if (KeyValue == "arguments" && !SequenceString) {
371*0b57cec5SDimitry Andric         ErrorMessage = "Expected sequence as value.";
372*0b57cec5SDimitry Andric         return false;
373*0b57cec5SDimitry Andric       } else if (KeyValue != "arguments" && !ValueString) {
374*0b57cec5SDimitry Andric         ErrorMessage = "Expected string as value.";
375*0b57cec5SDimitry Andric         return false;
376*0b57cec5SDimitry Andric       }
377*0b57cec5SDimitry Andric       if (KeyValue == "directory") {
378*0b57cec5SDimitry Andric         Directory = ValueString;
379*0b57cec5SDimitry Andric       } else if (KeyValue == "arguments") {
380*0b57cec5SDimitry Andric         Command = std::vector<llvm::yaml::ScalarNode *>();
381*0b57cec5SDimitry Andric         for (auto &Argument : *SequenceString) {
382*0b57cec5SDimitry Andric           auto *Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
383*0b57cec5SDimitry Andric           if (!Scalar) {
384*0b57cec5SDimitry Andric             ErrorMessage = "Only strings are allowed in 'arguments'.";
385*0b57cec5SDimitry Andric             return false;
386*0b57cec5SDimitry Andric           }
387*0b57cec5SDimitry Andric           Command->push_back(Scalar);
388*0b57cec5SDimitry Andric         }
389*0b57cec5SDimitry Andric       } else if (KeyValue == "command") {
390*0b57cec5SDimitry Andric         if (!Command)
391*0b57cec5SDimitry Andric           Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
392*0b57cec5SDimitry Andric       } else if (KeyValue == "file") {
393*0b57cec5SDimitry Andric         File = ValueString;
394*0b57cec5SDimitry Andric       } else if (KeyValue == "output") {
395*0b57cec5SDimitry Andric         Output = ValueString;
396*0b57cec5SDimitry Andric       } else {
397*0b57cec5SDimitry Andric         ErrorMessage = ("Unknown key: \"" +
398*0b57cec5SDimitry Andric                         KeyString->getRawValue() + "\"").str();
399*0b57cec5SDimitry Andric         return false;
400*0b57cec5SDimitry Andric       }
401*0b57cec5SDimitry Andric     }
402*0b57cec5SDimitry Andric     if (!File) {
403*0b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"file\".";
404*0b57cec5SDimitry Andric       return false;
405*0b57cec5SDimitry Andric     }
406*0b57cec5SDimitry Andric     if (!Command) {
407*0b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"command\" or \"arguments\".";
408*0b57cec5SDimitry Andric       return false;
409*0b57cec5SDimitry Andric     }
410*0b57cec5SDimitry Andric     if (!Directory) {
411*0b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"directory\".";
412*0b57cec5SDimitry Andric       return false;
413*0b57cec5SDimitry Andric     }
414*0b57cec5SDimitry Andric     SmallString<8> FileStorage;
415*0b57cec5SDimitry Andric     StringRef FileName = File->getValue(FileStorage);
416*0b57cec5SDimitry Andric     SmallString<128> NativeFilePath;
417*0b57cec5SDimitry Andric     if (llvm::sys::path::is_relative(FileName)) {
418*0b57cec5SDimitry Andric       SmallString<8> DirectoryStorage;
419*0b57cec5SDimitry Andric       SmallString<128> AbsolutePath(
420*0b57cec5SDimitry Andric           Directory->getValue(DirectoryStorage));
421*0b57cec5SDimitry Andric       llvm::sys::path::append(AbsolutePath, FileName);
422*0b57cec5SDimitry Andric       llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/ true);
423*0b57cec5SDimitry Andric       llvm::sys::path::native(AbsolutePath, NativeFilePath);
424*0b57cec5SDimitry Andric     } else {
425*0b57cec5SDimitry Andric       llvm::sys::path::native(FileName, NativeFilePath);
426*0b57cec5SDimitry Andric     }
427*0b57cec5SDimitry Andric     auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
428*0b57cec5SDimitry Andric     IndexByFile[NativeFilePath].push_back(Cmd);
429*0b57cec5SDimitry Andric     AllCommands.push_back(Cmd);
430*0b57cec5SDimitry Andric     MatchTrie.insert(NativeFilePath);
431*0b57cec5SDimitry Andric   }
432*0b57cec5SDimitry Andric   return true;
433*0b57cec5SDimitry Andric }
434