10b57cec5SDimitry Andric //===- JSONCompilationDatabase.cpp ----------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file contains the implementation of the JSONCompilationDatabase.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "clang/Tooling/JSONCompilationDatabase.h"
140b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
150b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabase.h"
160b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
170b57cec5SDimitry Andric #include "clang/Tooling/Tooling.h"
180b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
190b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
210b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
220b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
230b57cec5SDimitry Andric #include "llvm/ADT/Triple.h"
240b57cec5SDimitry Andric #include "llvm/Support/Allocator.h"
250b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
260b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
270b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
280b57cec5SDimitry Andric #include "llvm/Support/Host.h"
290b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
300b57cec5SDimitry Andric #include "llvm/Support/Path.h"
310b57cec5SDimitry Andric #include "llvm/Support/StringSaver.h"
32480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
330b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h"
340b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
350b57cec5SDimitry Andric #include <cassert>
360b57cec5SDimitry Andric #include <memory>
370b57cec5SDimitry Andric #include <string>
380b57cec5SDimitry Andric #include <system_error>
390b57cec5SDimitry Andric #include <tuple>
400b57cec5SDimitry Andric #include <utility>
410b57cec5SDimitry Andric #include <vector>
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric using namespace clang;
440b57cec5SDimitry Andric using namespace tooling;
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric namespace {
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric /// A parser for escaped strings of command line arguments.
490b57cec5SDimitry Andric ///
500b57cec5SDimitry Andric /// Assumes \-escaping for quoted arguments (see the documentation of
510b57cec5SDimitry Andric /// unescapeCommandLine(...)).
520b57cec5SDimitry Andric class CommandLineArgumentParser {
530b57cec5SDimitry Andric  public:
CommandLineArgumentParser(StringRef CommandLine)540b57cec5SDimitry Andric   CommandLineArgumentParser(StringRef CommandLine)
550b57cec5SDimitry Andric       : Input(CommandLine), Position(Input.begin()-1) {}
560b57cec5SDimitry Andric 
parse()570b57cec5SDimitry Andric   std::vector<std::string> parse() {
580b57cec5SDimitry Andric     bool HasMoreInput = true;
590b57cec5SDimitry Andric     while (HasMoreInput && nextNonWhitespace()) {
600b57cec5SDimitry Andric       std::string Argument;
610b57cec5SDimitry Andric       HasMoreInput = parseStringInto(Argument);
620b57cec5SDimitry Andric       CommandLine.push_back(Argument);
630b57cec5SDimitry Andric     }
640b57cec5SDimitry Andric     return CommandLine;
650b57cec5SDimitry Andric   }
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric  private:
680b57cec5SDimitry Andric   // All private methods return true if there is more input available.
690b57cec5SDimitry Andric 
parseStringInto(std::string & String)700b57cec5SDimitry Andric   bool parseStringInto(std::string &String) {
710b57cec5SDimitry Andric     do {
720b57cec5SDimitry Andric       if (*Position == '"') {
730b57cec5SDimitry Andric         if (!parseDoubleQuotedStringInto(String)) return false;
740b57cec5SDimitry Andric       } else if (*Position == '\'') {
750b57cec5SDimitry Andric         if (!parseSingleQuotedStringInto(String)) return false;
760b57cec5SDimitry Andric       } else {
770b57cec5SDimitry Andric         if (!parseFreeStringInto(String)) return false;
780b57cec5SDimitry Andric       }
790b57cec5SDimitry Andric     } while (*Position != ' ');
800b57cec5SDimitry Andric     return true;
810b57cec5SDimitry Andric   }
820b57cec5SDimitry Andric 
parseDoubleQuotedStringInto(std::string & String)830b57cec5SDimitry Andric   bool parseDoubleQuotedStringInto(std::string &String) {
840b57cec5SDimitry Andric     if (!next()) return false;
850b57cec5SDimitry Andric     while (*Position != '"') {
860b57cec5SDimitry Andric       if (!skipEscapeCharacter()) return false;
870b57cec5SDimitry Andric       String.push_back(*Position);
880b57cec5SDimitry Andric       if (!next()) return false;
890b57cec5SDimitry Andric     }
900b57cec5SDimitry Andric     return next();
910b57cec5SDimitry Andric   }
920b57cec5SDimitry Andric 
parseSingleQuotedStringInto(std::string & String)930b57cec5SDimitry Andric   bool parseSingleQuotedStringInto(std::string &String) {
940b57cec5SDimitry Andric     if (!next()) return false;
950b57cec5SDimitry Andric     while (*Position != '\'') {
960b57cec5SDimitry Andric       String.push_back(*Position);
970b57cec5SDimitry Andric       if (!next()) return false;
980b57cec5SDimitry Andric     }
990b57cec5SDimitry Andric     return next();
1000b57cec5SDimitry Andric   }
1010b57cec5SDimitry Andric 
parseFreeStringInto(std::string & String)1020b57cec5SDimitry Andric   bool parseFreeStringInto(std::string &String) {
1030b57cec5SDimitry Andric     do {
1040b57cec5SDimitry Andric       if (!skipEscapeCharacter()) return false;
1050b57cec5SDimitry Andric       String.push_back(*Position);
1060b57cec5SDimitry Andric       if (!next()) return false;
1070b57cec5SDimitry Andric     } while (*Position != ' ' && *Position != '"' && *Position != '\'');
1080b57cec5SDimitry Andric     return true;
1090b57cec5SDimitry Andric   }
1100b57cec5SDimitry Andric 
skipEscapeCharacter()1110b57cec5SDimitry Andric   bool skipEscapeCharacter() {
1120b57cec5SDimitry Andric     if (*Position == '\\') {
1130b57cec5SDimitry Andric       return next();
1140b57cec5SDimitry Andric     }
1150b57cec5SDimitry Andric     return true;
1160b57cec5SDimitry Andric   }
1170b57cec5SDimitry Andric 
nextNonWhitespace()1180b57cec5SDimitry Andric   bool nextNonWhitespace() {
1190b57cec5SDimitry Andric     do {
1200b57cec5SDimitry Andric       if (!next()) return false;
1210b57cec5SDimitry Andric     } while (*Position == ' ');
1220b57cec5SDimitry Andric     return true;
1230b57cec5SDimitry Andric   }
1240b57cec5SDimitry Andric 
next()1250b57cec5SDimitry Andric   bool next() {
1260b57cec5SDimitry Andric     ++Position;
1270b57cec5SDimitry Andric     return Position != Input.end();
1280b57cec5SDimitry Andric   }
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   const StringRef Input;
1310b57cec5SDimitry Andric   StringRef::iterator Position;
1320b57cec5SDimitry Andric   std::vector<std::string> CommandLine;
1330b57cec5SDimitry Andric };
1340b57cec5SDimitry Andric 
unescapeCommandLine(JSONCommandLineSyntax Syntax,StringRef EscapedCommandLine)1350b57cec5SDimitry Andric std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax,
1360b57cec5SDimitry Andric                                              StringRef EscapedCommandLine) {
1370b57cec5SDimitry Andric   if (Syntax == JSONCommandLineSyntax::AutoDetect) {
1380b57cec5SDimitry Andric     Syntax = JSONCommandLineSyntax::Gnu;
1390b57cec5SDimitry Andric     llvm::Triple Triple(llvm::sys::getProcessTriple());
1400b57cec5SDimitry Andric     if (Triple.getOS() == llvm::Triple::OSType::Win32) {
1410b57cec5SDimitry Andric       // Assume Windows command line parsing on Win32 unless the triple
1420b57cec5SDimitry Andric       // explicitly tells us otherwise.
1430b57cec5SDimitry Andric       if (!Triple.hasEnvironment() ||
1440b57cec5SDimitry Andric           Triple.getEnvironment() == llvm::Triple::EnvironmentType::MSVC)
1450b57cec5SDimitry Andric         Syntax = JSONCommandLineSyntax::Windows;
1460b57cec5SDimitry Andric     }
1470b57cec5SDimitry Andric   }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric   if (Syntax == JSONCommandLineSyntax::Windows) {
1500b57cec5SDimitry Andric     llvm::BumpPtrAllocator Alloc;
1510b57cec5SDimitry Andric     llvm::StringSaver Saver(Alloc);
1520b57cec5SDimitry Andric     llvm::SmallVector<const char *, 64> T;
1530b57cec5SDimitry Andric     llvm::cl::TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T);
1540b57cec5SDimitry Andric     std::vector<std::string> Result(T.begin(), T.end());
1550b57cec5SDimitry Andric     return Result;
1560b57cec5SDimitry Andric   }
1570b57cec5SDimitry Andric   assert(Syntax == JSONCommandLineSyntax::Gnu);
1580b57cec5SDimitry Andric   CommandLineArgumentParser parser(EscapedCommandLine);
1590b57cec5SDimitry Andric   return parser.parse();
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric // This plugin locates a nearby compile_command.json file, and also infers
1630b57cec5SDimitry Andric // compile commands for files not present in the database.
1640b57cec5SDimitry Andric class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
1650b57cec5SDimitry Andric   std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)1660b57cec5SDimitry Andric   loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
1670b57cec5SDimitry Andric     SmallString<1024> JSONDatabasePath(Directory);
1680b57cec5SDimitry Andric     llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
1690b57cec5SDimitry Andric     auto Base = JSONCompilationDatabase::loadFromFile(
1700b57cec5SDimitry Andric         JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect);
1710b57cec5SDimitry Andric     return Base ? inferTargetAndDriverMode(
172480093f4SDimitry Andric                       inferMissingCompileCommands(expandResponseFiles(
173480093f4SDimitry Andric                           std::move(Base), llvm::vfs::getRealFileSystem())))
1740b57cec5SDimitry Andric                 : nullptr;
1750b57cec5SDimitry Andric   }
1760b57cec5SDimitry Andric };
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric } // namespace
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric // Register the JSONCompilationDatabasePlugin with the
1810b57cec5SDimitry Andric // CompilationDatabasePluginRegistry using this statically initialized variable.
1820b57cec5SDimitry Andric static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
1830b57cec5SDimitry Andric X("json-compilation-database", "Reads JSON formatted compilation databases");
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric namespace clang {
1860b57cec5SDimitry Andric namespace tooling {
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file
1890b57cec5SDimitry Andric // and thus register the JSONCompilationDatabasePlugin.
1900b57cec5SDimitry Andric volatile int JSONAnchorSource = 0;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric } // namespace tooling
1930b57cec5SDimitry Andric } // namespace clang
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase>
loadFromFile(StringRef FilePath,std::string & ErrorMessage,JSONCommandLineSyntax Syntax)1960b57cec5SDimitry Andric JSONCompilationDatabase::loadFromFile(StringRef FilePath,
1970b57cec5SDimitry Andric                                       std::string &ErrorMessage,
1980b57cec5SDimitry Andric                                       JSONCommandLineSyntax Syntax) {
1990b57cec5SDimitry Andric   // Don't mmap: if we're a long-lived process, the build system may overwrite.
2000b57cec5SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
201*5f7ddb14SDimitry Andric       llvm::MemoryBuffer::getFile(FilePath, /*IsText=*/false,
2020b57cec5SDimitry Andric                                   /*RequiresNullTerminator=*/true,
2030b57cec5SDimitry Andric                                   /*IsVolatile=*/true);
2040b57cec5SDimitry Andric   if (std::error_code Result = DatabaseBuffer.getError()) {
2050b57cec5SDimitry Andric     ErrorMessage = "Error while opening JSON database: " + Result.message();
2060b57cec5SDimitry Andric     return nullptr;
2070b57cec5SDimitry Andric   }
2080b57cec5SDimitry Andric   std::unique_ptr<JSONCompilationDatabase> Database(
2090b57cec5SDimitry Andric       new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax));
2100b57cec5SDimitry Andric   if (!Database->parse(ErrorMessage))
2110b57cec5SDimitry Andric     return nullptr;
2120b57cec5SDimitry Andric   return Database;
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric 
2150b57cec5SDimitry Andric std::unique_ptr<JSONCompilationDatabase>
loadFromBuffer(StringRef DatabaseString,std::string & ErrorMessage,JSONCommandLineSyntax Syntax)2160b57cec5SDimitry Andric JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
2170b57cec5SDimitry Andric                                         std::string &ErrorMessage,
2180b57cec5SDimitry Andric                                         JSONCommandLineSyntax Syntax) {
2190b57cec5SDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
220af732203SDimitry Andric       llvm::MemoryBuffer::getMemBufferCopy(DatabaseString));
2210b57cec5SDimitry Andric   std::unique_ptr<JSONCompilationDatabase> Database(
2220b57cec5SDimitry Andric       new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax));
2230b57cec5SDimitry Andric   if (!Database->parse(ErrorMessage))
2240b57cec5SDimitry Andric     return nullptr;
2250b57cec5SDimitry Andric   return Database;
2260b57cec5SDimitry Andric }
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const2290b57cec5SDimitry Andric JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
2300b57cec5SDimitry Andric   SmallString<128> NativeFilePath;
2310b57cec5SDimitry Andric   llvm::sys::path::native(FilePath, NativeFilePath);
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric   std::string Error;
2340b57cec5SDimitry Andric   llvm::raw_string_ostream ES(Error);
2350b57cec5SDimitry Andric   StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
2360b57cec5SDimitry Andric   if (Match.empty())
2370b57cec5SDimitry Andric     return {};
2380b57cec5SDimitry Andric   const auto CommandsRefI = IndexByFile.find(Match);
2390b57cec5SDimitry Andric   if (CommandsRefI == IndexByFile.end())
2400b57cec5SDimitry Andric     return {};
2410b57cec5SDimitry Andric   std::vector<CompileCommand> Commands;
2420b57cec5SDimitry Andric   getCommands(CommandsRefI->getValue(), Commands);
2430b57cec5SDimitry Andric   return Commands;
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric std::vector<std::string>
getAllFiles() const2470b57cec5SDimitry Andric JSONCompilationDatabase::getAllFiles() const {
2480b57cec5SDimitry Andric   std::vector<std::string> Result;
2490b57cec5SDimitry Andric   for (const auto &CommandRef : IndexByFile)
2500b57cec5SDimitry Andric     Result.push_back(CommandRef.first().str());
2510b57cec5SDimitry Andric   return Result;
2520b57cec5SDimitry Andric }
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric std::vector<CompileCommand>
getAllCompileCommands() const2550b57cec5SDimitry Andric JSONCompilationDatabase::getAllCompileCommands() const {
2560b57cec5SDimitry Andric   std::vector<CompileCommand> Commands;
2570b57cec5SDimitry Andric   getCommands(AllCommands, Commands);
2580b57cec5SDimitry Andric   return Commands;
2590b57cec5SDimitry Andric }
2600b57cec5SDimitry Andric 
stripExecutableExtension(llvm::StringRef Name)2610b57cec5SDimitry Andric static llvm::StringRef stripExecutableExtension(llvm::StringRef Name) {
2620b57cec5SDimitry Andric   Name.consume_back(".exe");
2630b57cec5SDimitry Andric   return Name;
2640b57cec5SDimitry Andric }
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric // There are compiler-wrappers (ccache, distcc, gomacc) that take the "real"
2670b57cec5SDimitry Andric // compiler as an argument, e.g. distcc gcc -O3 foo.c.
2680b57cec5SDimitry Andric // These end up in compile_commands.json when people set CC="distcc gcc".
2690b57cec5SDimitry Andric // Clang's driver doesn't understand this, so we need to unwrap.
unwrapCommand(std::vector<std::string> & Args)2700b57cec5SDimitry Andric static bool unwrapCommand(std::vector<std::string> &Args) {
2710b57cec5SDimitry Andric   if (Args.size() < 2)
2720b57cec5SDimitry Andric     return false;
2730b57cec5SDimitry Andric   StringRef Wrapper =
2740b57cec5SDimitry Andric       stripExecutableExtension(llvm::sys::path::filename(Args.front()));
275af732203SDimitry Andric   if (Wrapper == "distcc" || Wrapper == "gomacc" || Wrapper == "ccache" ||
276af732203SDimitry Andric       Wrapper == "sccache") {
2770b57cec5SDimitry Andric     // Most of these wrappers support being invoked 3 ways:
2780b57cec5SDimitry Andric     // `distcc g++ file.c` This is the mode we're trying to match.
2790b57cec5SDimitry Andric     //                     We need to drop `distcc`.
2800b57cec5SDimitry Andric     // `distcc file.c`     This acts like compiler is cc or similar.
2810b57cec5SDimitry Andric     //                     Clang's driver can handle this, no change needed.
2820b57cec5SDimitry Andric     // `g++ file.c`        g++ is a symlink to distcc.
2830b57cec5SDimitry Andric     //                     We don't even notice this case, and all is well.
2840b57cec5SDimitry Andric     //
2850b57cec5SDimitry Andric     // We need to distinguish between the first and second case.
2860b57cec5SDimitry Andric     // The wrappers themselves don't take flags, so Args[1] is a compiler flag,
2870b57cec5SDimitry Andric     // an input file, or a compiler. Inputs have extensions, compilers don't.
2880b57cec5SDimitry Andric     bool HasCompiler =
2890b57cec5SDimitry Andric         (Args[1][0] != '-') &&
2900b57cec5SDimitry Andric         !llvm::sys::path::has_extension(stripExecutableExtension(Args[1]));
2910b57cec5SDimitry Andric     if (HasCompiler) {
2920b57cec5SDimitry Andric       Args.erase(Args.begin());
2930b57cec5SDimitry Andric       return true;
2940b57cec5SDimitry Andric     }
2950b57cec5SDimitry Andric     // If !HasCompiler, wrappers act like GCC. Fine: so do we.
2960b57cec5SDimitry Andric   }
2970b57cec5SDimitry Andric   return false;
2980b57cec5SDimitry Andric }
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric static std::vector<std::string>
nodeToCommandLine(JSONCommandLineSyntax Syntax,const std::vector<llvm::yaml::ScalarNode * > & Nodes)3010b57cec5SDimitry Andric nodeToCommandLine(JSONCommandLineSyntax Syntax,
3020b57cec5SDimitry Andric                   const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
3030b57cec5SDimitry Andric   SmallString<1024> Storage;
3040b57cec5SDimitry Andric   std::vector<std::string> Arguments;
3050b57cec5SDimitry Andric   if (Nodes.size() == 1)
3060b57cec5SDimitry Andric     Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
3070b57cec5SDimitry Andric   else
3080b57cec5SDimitry Andric     for (const auto *Node : Nodes)
3095ffd83dbSDimitry Andric       Arguments.push_back(std::string(Node->getValue(Storage)));
3100b57cec5SDimitry Andric   // There may be multiple wrappers: using distcc and ccache together is common.
3110b57cec5SDimitry Andric   while (unwrapCommand(Arguments))
3120b57cec5SDimitry Andric     ;
3130b57cec5SDimitry Andric   return Arguments;
3140b57cec5SDimitry Andric }
3150b57cec5SDimitry Andric 
getCommands(ArrayRef<CompileCommandRef> CommandsRef,std::vector<CompileCommand> & Commands) const3160b57cec5SDimitry Andric void JSONCompilationDatabase::getCommands(
3170b57cec5SDimitry Andric     ArrayRef<CompileCommandRef> CommandsRef,
3180b57cec5SDimitry Andric     std::vector<CompileCommand> &Commands) const {
3190b57cec5SDimitry Andric   for (const auto &CommandRef : CommandsRef) {
3200b57cec5SDimitry Andric     SmallString<8> DirectoryStorage;
3210b57cec5SDimitry Andric     SmallString<32> FilenameStorage;
3220b57cec5SDimitry Andric     SmallString<32> OutputStorage;
3230b57cec5SDimitry Andric     auto Output = std::get<3>(CommandRef);
3240b57cec5SDimitry Andric     Commands.emplace_back(
3250b57cec5SDimitry Andric         std::get<0>(CommandRef)->getValue(DirectoryStorage),
3260b57cec5SDimitry Andric         std::get<1>(CommandRef)->getValue(FilenameStorage),
3270b57cec5SDimitry Andric         nodeToCommandLine(Syntax, std::get<2>(CommandRef)),
3280b57cec5SDimitry Andric         Output ? Output->getValue(OutputStorage) : "");
3290b57cec5SDimitry Andric   }
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric 
parse(std::string & ErrorMessage)3320b57cec5SDimitry Andric bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
3330b57cec5SDimitry Andric   llvm::yaml::document_iterator I = YAMLStream.begin();
3340b57cec5SDimitry Andric   if (I == YAMLStream.end()) {
3350b57cec5SDimitry Andric     ErrorMessage = "Error while parsing YAML.";
3360b57cec5SDimitry Andric     return false;
3370b57cec5SDimitry Andric   }
3380b57cec5SDimitry Andric   llvm::yaml::Node *Root = I->getRoot();
3390b57cec5SDimitry Andric   if (!Root) {
3400b57cec5SDimitry Andric     ErrorMessage = "Error while parsing YAML.";
3410b57cec5SDimitry Andric     return false;
3420b57cec5SDimitry Andric   }
3430b57cec5SDimitry Andric   auto *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
3440b57cec5SDimitry Andric   if (!Array) {
3450b57cec5SDimitry Andric     ErrorMessage = "Expected array.";
3460b57cec5SDimitry Andric     return false;
3470b57cec5SDimitry Andric   }
3480b57cec5SDimitry Andric   for (auto &NextObject : *Array) {
3490b57cec5SDimitry Andric     auto *Object = dyn_cast<llvm::yaml::MappingNode>(&NextObject);
3500b57cec5SDimitry Andric     if (!Object) {
3510b57cec5SDimitry Andric       ErrorMessage = "Expected object.";
3520b57cec5SDimitry Andric       return false;
3530b57cec5SDimitry Andric     }
3540b57cec5SDimitry Andric     llvm::yaml::ScalarNode *Directory = nullptr;
3550b57cec5SDimitry Andric     llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
3560b57cec5SDimitry Andric     llvm::yaml::ScalarNode *File = nullptr;
3570b57cec5SDimitry Andric     llvm::yaml::ScalarNode *Output = nullptr;
3580b57cec5SDimitry Andric     for (auto& NextKeyValue : *Object) {
3590b57cec5SDimitry Andric       auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
3600b57cec5SDimitry Andric       if (!KeyString) {
3610b57cec5SDimitry Andric         ErrorMessage = "Expected strings as key.";
3620b57cec5SDimitry Andric         return false;
3630b57cec5SDimitry Andric       }
3640b57cec5SDimitry Andric       SmallString<10> KeyStorage;
3650b57cec5SDimitry Andric       StringRef KeyValue = KeyString->getValue(KeyStorage);
3660b57cec5SDimitry Andric       llvm::yaml::Node *Value = NextKeyValue.getValue();
3670b57cec5SDimitry Andric       if (!Value) {
3680b57cec5SDimitry Andric         ErrorMessage = "Expected value.";
3690b57cec5SDimitry Andric         return false;
3700b57cec5SDimitry Andric       }
3710b57cec5SDimitry Andric       auto *ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value);
3720b57cec5SDimitry Andric       auto *SequenceString = dyn_cast<llvm::yaml::SequenceNode>(Value);
373af732203SDimitry Andric       if (KeyValue == "arguments") {
374af732203SDimitry Andric         if (!SequenceString) {
3750b57cec5SDimitry Andric           ErrorMessage = "Expected sequence as value.";
3760b57cec5SDimitry Andric           return false;
3770b57cec5SDimitry Andric         }
3780b57cec5SDimitry Andric         Command = std::vector<llvm::yaml::ScalarNode *>();
3790b57cec5SDimitry Andric         for (auto &Argument : *SequenceString) {
3800b57cec5SDimitry Andric           auto *Scalar = dyn_cast<llvm::yaml::ScalarNode>(&Argument);
3810b57cec5SDimitry Andric           if (!Scalar) {
3820b57cec5SDimitry Andric             ErrorMessage = "Only strings are allowed in 'arguments'.";
3830b57cec5SDimitry Andric             return false;
3840b57cec5SDimitry Andric           }
3850b57cec5SDimitry Andric           Command->push_back(Scalar);
3860b57cec5SDimitry Andric         }
387af732203SDimitry Andric       } else {
388af732203SDimitry Andric         if (!ValueString) {
389af732203SDimitry Andric           ErrorMessage = "Expected string as value.";
390af732203SDimitry Andric           return false;
391af732203SDimitry Andric         }
392af732203SDimitry Andric         if (KeyValue == "directory") {
393af732203SDimitry Andric           Directory = ValueString;
3940b57cec5SDimitry Andric         } else if (KeyValue == "command") {
3950b57cec5SDimitry Andric           if (!Command)
3960b57cec5SDimitry Andric             Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
3970b57cec5SDimitry Andric         } else if (KeyValue == "file") {
3980b57cec5SDimitry Andric           File = ValueString;
3990b57cec5SDimitry Andric         } else if (KeyValue == "output") {
4000b57cec5SDimitry Andric           Output = ValueString;
4010b57cec5SDimitry Andric         } else {
402af732203SDimitry Andric           ErrorMessage =
403af732203SDimitry Andric               ("Unknown key: \"" + KeyString->getRawValue() + "\"").str();
4040b57cec5SDimitry Andric           return false;
4050b57cec5SDimitry Andric         }
4060b57cec5SDimitry Andric       }
407af732203SDimitry Andric     }
4080b57cec5SDimitry Andric     if (!File) {
4090b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"file\".";
4100b57cec5SDimitry Andric       return false;
4110b57cec5SDimitry Andric     }
4120b57cec5SDimitry Andric     if (!Command) {
4130b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"command\" or \"arguments\".";
4140b57cec5SDimitry Andric       return false;
4150b57cec5SDimitry Andric     }
4160b57cec5SDimitry Andric     if (!Directory) {
4170b57cec5SDimitry Andric       ErrorMessage = "Missing key: \"directory\".";
4180b57cec5SDimitry Andric       return false;
4190b57cec5SDimitry Andric     }
4200b57cec5SDimitry Andric     SmallString<8> FileStorage;
4210b57cec5SDimitry Andric     StringRef FileName = File->getValue(FileStorage);
4220b57cec5SDimitry Andric     SmallString<128> NativeFilePath;
4230b57cec5SDimitry Andric     if (llvm::sys::path::is_relative(FileName)) {
4240b57cec5SDimitry Andric       SmallString<8> DirectoryStorage;
4250b57cec5SDimitry Andric       SmallString<128> AbsolutePath(
4260b57cec5SDimitry Andric           Directory->getValue(DirectoryStorage));
4270b57cec5SDimitry Andric       llvm::sys::path::append(AbsolutePath, FileName);
4280b57cec5SDimitry Andric       llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/ true);
4290b57cec5SDimitry Andric       llvm::sys::path::native(AbsolutePath, NativeFilePath);
4300b57cec5SDimitry Andric     } else {
4310b57cec5SDimitry Andric       llvm::sys::path::native(FileName, NativeFilePath);
4320b57cec5SDimitry Andric     }
4330b57cec5SDimitry Andric     auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
4340b57cec5SDimitry Andric     IndexByFile[NativeFilePath].push_back(Cmd);
4350b57cec5SDimitry Andric     AllCommands.push_back(Cmd);
4360b57cec5SDimitry Andric     MatchTrie.insert(NativeFilePath);
4370b57cec5SDimitry Andric   }
4380b57cec5SDimitry Andric   return true;
4390b57cec5SDimitry Andric }
440