Index: ../llvm/tools/clang/include/clang/Tooling/JSONCompilationDatabase.h =================================================================== --- ../llvm/tools/clang/include/clang/Tooling/JSONCompilationDatabase.h +++ ../llvm/tools/clang/include/clang/Tooling/JSONCompilationDatabase.h @@ -33,18 +33,26 @@ /// \brief A JSON based compilation database. /// /// JSON compilation database files must contain a list of JSON objects which -/// provide the command lines in the attributes 'directory', 'command' and -/// 'file': +/// provide the command lines in the attributes 'directory', 'command', +/// 'arguments' and 'file': /// [ /// { "directory": "", /// "command": "", /// "file": "" /// }, +/// { "directory": "", +/// "arguments": ["", "" "" ""], +/// "file": "" +/// }, /// ... /// ] /// Each object entry defines one compile action. The specified file is /// considered to be the main source file for the translation unit. /// +/// 'command' is a full command line that will be unescaped. +/// +/// 'arguments' is a list of command line arguments that will not be unescaped. +/// /// JSON compilation databases can for example be generated in CMake projects /// by setting the flag -DCMAKE_EXPORT_COMPILE_COMMANDS. class JSONCompilationDatabase : public CompilationDatabase { @@ -94,7 +102,7 @@ // Tuple (directory, commandline) where 'commandline' pointing to the // corresponding nodes in the YAML stream. typedef std::pair CompileCommandRef; + std::vector> CompileCommandRef; /// \brief Converts the given array of CompileCommandRefs to CompileCommands. void getCommands(ArrayRef CommandsRef, Index: ../llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp =================================================================== --- ../llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ ../llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -221,9 +221,8 @@ SmallString<8> DirectoryStorage; SmallString<1024> CommandStorage; Commands.emplace_back( - // FIXME: Escape correctly: - CommandsRef[I].first->getValue(DirectoryStorage), - unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))); + CommandsRef[I].first->getValue(DirectoryStorage), + CommandsRef[I].second); } } @@ -243,44 +242,66 @@ ErrorMessage = "Expected array."; return false; } - for (llvm::yaml::SequenceNode::iterator AI = Array->begin(), - AE = Array->end(); - AI != AE; ++AI) { - llvm::yaml::MappingNode *Object = dyn_cast(&*AI); + for (auto& NextObject : *Array) { + llvm::yaml::MappingNode *Object = dyn_cast(&NextObject); if (!Object) { ErrorMessage = "Expected object."; return false; } llvm::yaml::ScalarNode *Directory = nullptr; - llvm::yaml::ScalarNode *Command = nullptr; + std::vector Args; + bool CommandFound = false; llvm::yaml::ScalarNode *File = nullptr; - for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), - KVE = Object->end(); - KVI != KVE; ++KVI) { - llvm::yaml::Node *Value = (*KVI).getValue(); + for (auto& NextKeyValue : *Object) { + llvm::yaml::ScalarNode *KeyString = + dyn_cast(NextKeyValue.getKey()); + if (!KeyString) { + ErrorMessage = "Expected strings as key."; + return false; + } + SmallString<8> KeyStorage; + StringRef KeyValue = KeyString->getValue(KeyStorage); + llvm::yaml::Node *Value = NextKeyValue.getValue(); if (!Value) { ErrorMessage = "Expected value."; return false; } llvm::yaml::ScalarNode *ValueString = dyn_cast(Value); - if (!ValueString) { - ErrorMessage = "Expected string as value."; + llvm::yaml::SequenceNode *SequenceString = + dyn_cast(Value); + if (KeyValue == "arguments" && !SequenceString) { + ErrorMessage = "Expected sequence as value."; return false; - } - llvm::yaml::ScalarNode *KeyString = - dyn_cast((*KVI).getKey()); - if (!KeyString) { - ErrorMessage = "Expected strings as key."; + } else if (KeyValue != "arguments" && !ValueString) { + ErrorMessage = "Expected string as value."; return false; } - SmallString<8> KeyStorage; - if (KeyString->getValue(KeyStorage) == "directory") { + if (KeyValue == "directory") { Directory = ValueString; - } else if (KeyString->getValue(KeyStorage) == "command") { - Command = ValueString; - } else if (KeyString->getValue(KeyStorage) == "file") { + } else if (KeyValue == "command") { + if (CommandFound) { + ErrorMessage = "Multiple command and arguments found"; + return false; + } + SmallString<1024> CommandStorage; + // FIXME: Escape correctly: + Args = unescapeCommandLine(ValueString->getValue(CommandStorage)); + CommandFound = true; + } else if (KeyValue == "file") { File = ValueString; + } else if (KeyValue == "arguments") { + if (CommandFound) { + ErrorMessage = "Multiple command and arguments found"; + return false; + } + for (auto& NextArgument : *SequenceString) { + SmallString<128> CommandStorage; + auto ValueString = dyn_cast(&NextArgument); + + Args.push_back(ValueString->getValue(CommandStorage)); + } + CommandFound = true; } else { ErrorMessage = ("Unknown key: \"" + KeyString->getRawValue() + "\"").str(); @@ -291,8 +312,8 @@ ErrorMessage = "Missing key: \"file\"."; return false; } - if (!Command) { - ErrorMessage = "Missing key: \"command\"."; + if (!CommandFound) { + ErrorMessage = "Missing key: \"command\" or \"arguments\"."; return false; } if (!Directory) { @@ -312,7 +333,7 @@ llvm::sys::path::native(FileName, NativeFilePath); } IndexByFile[NativeFilePath].push_back( - CompileCommandRef(Directory, Command)); + CompileCommandRef(Directory, Args)); MatchTrie.insert(NativeFilePath); } return true; Index: ../llvm/tools/clang/unittests/Tooling/CompilationDatabaseTest.cpp =================================================================== --- ../llvm/tools/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ ../llvm/tools/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -36,8 +36,13 @@ expectFailure("[{[]:\"\"}]", "Incorrectly typed entry"); expectFailure("[{}]", "Empty entry"); expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file"); - expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command"); + expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command or arguments"); expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory"); + expectFailure("[{\"directory\":\"\",\"arguments\":[]}]", "Missing file"); + expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory"); + expectFailure("[{\"command\":\"\",\"arguments\":[],\"file\":\"\"}]", "Command and arguments"); + expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array"); + expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string"); } static std::vector getAllFiles(StringRef JSONDatabase,