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