2012-04-04 20:07:46 +08:00
|
|
|
//===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2012-04-04 20:07:46 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
|
|
#include "clang/AST/DeclGroup.h"
|
|
|
|
#include "clang/Frontend/FrontendAction.h"
|
2019-06-26 15:39:03 +08:00
|
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
2012-10-09 00:08:15 +08:00
|
|
|
#include "clang/Tooling/FileMatchTrie.h"
|
2012-08-24 13:50:27 +08:00
|
|
|
#include "clang/Tooling/JSONCompilationDatabase.h"
|
2012-04-04 20:07:46 +08:00
|
|
|
#include "clang/Tooling/Tooling.h"
|
2013-06-12 06:15:02 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
2019-06-26 15:39:03 +08:00
|
|
|
#include "llvm/Support/TargetSelect.h"
|
2019-01-16 17:41:26 +08:00
|
|
|
#include "gmock/gmock.h"
|
2012-04-04 20:07:46 +08:00
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace tooling {
|
|
|
|
|
2019-01-16 17:41:26 +08:00
|
|
|
using testing::ElementsAre;
|
|
|
|
using testing::EndsWith;
|
|
|
|
|
2012-05-15 19:46:07 +08:00
|
|
|
static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
|
|
|
|
std::string ErrorMessage;
|
2016-08-19 03:31:48 +08:00
|
|
|
EXPECT_EQ(nullptr,
|
|
|
|
JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
|
|
|
|
JSONCommandLineSyntax::Gnu))
|
|
|
|
<< "Expected an error because of: " << Explanation.str();
|
2012-05-15 19:46:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
|
|
|
|
expectFailure("", "Empty database");
|
|
|
|
expectFailure("{", "Invalid JSON");
|
|
|
|
expectFailure("[[]]", "Array instead of object");
|
|
|
|
expectFailure("[{\"a\":[]}]", "Array instead of value");
|
|
|
|
expectFailure("[{\"a\":\"b\"}]", "Unknown key");
|
|
|
|
expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
|
|
|
|
expectFailure("[{}]", "Empty entry");
|
|
|
|
expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
|
2015-08-14 17:55:36 +08:00
|
|
|
expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command or arguments");
|
2012-05-15 19:46:07 +08:00
|
|
|
expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
|
2015-08-14 17:55:36 +08:00
|
|
|
expectFailure("[{\"directory\":\"\",\"arguments\":[]}]", "Missing file");
|
|
|
|
expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory");
|
|
|
|
expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array");
|
|
|
|
expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string");
|
2015-09-08 23:14:06 +08:00
|
|
|
expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]",
|
|
|
|
"Arguments contain non-string");
|
2016-12-02 07:37:45 +08:00
|
|
|
expectFailure("[{\"output\":[]}]", "Expected strings as value.");
|
2012-05-15 19:46:07 +08:00
|
|
|
}
|
|
|
|
|
2012-07-13 20:31:45 +08:00
|
|
|
static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
|
2016-08-19 03:31:48 +08:00
|
|
|
std::string &ErrorMessage,
|
|
|
|
JSONCommandLineSyntax Syntax) {
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<CompilationDatabase> Database(
|
2016-08-19 03:31:48 +08:00
|
|
|
JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
|
|
|
|
Syntax));
|
2012-07-13 20:31:45 +08:00
|
|
|
if (!Database) {
|
|
|
|
ADD_FAILURE() << ErrorMessage;
|
|
|
|
return std::vector<std::string>();
|
|
|
|
}
|
|
|
|
return Database->getAllFiles();
|
|
|
|
}
|
|
|
|
|
2016-08-19 03:31:48 +08:00
|
|
|
static std::vector<CompileCommand>
|
|
|
|
getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase,
|
|
|
|
std::string &ErrorMessage) {
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<CompilationDatabase> Database(
|
2016-08-19 03:31:48 +08:00
|
|
|
JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
|
|
|
|
Syntax));
|
2012-12-04 15:26:44 +08:00
|
|
|
if (!Database) {
|
|
|
|
ADD_FAILURE() << ErrorMessage;
|
|
|
|
return std::vector<CompileCommand>();
|
|
|
|
}
|
|
|
|
return Database->getAllCompileCommands();
|
|
|
|
}
|
|
|
|
|
2012-07-13 20:31:45 +08:00
|
|
|
TEST(JSONCompilationDatabase, GetAllFiles) {
|
|
|
|
std::string ErrorMessage;
|
|
|
|
EXPECT_EQ(std::vector<std::string>(),
|
2016-08-19 03:31:48 +08:00
|
|
|
getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu))
|
|
|
|
<< ErrorMessage;
|
2012-07-13 20:31:45 +08:00
|
|
|
|
|
|
|
std::vector<std::string> expected_files;
|
2012-10-09 04:32:51 +08:00
|
|
|
SmallString<16> PathStorage;
|
2017-04-27 06:45:04 +08:00
|
|
|
llvm::sys::path::native("//net/dir/file1", PathStorage);
|
2020-01-29 03:23:46 +08:00
|
|
|
expected_files.push_back(std::string(PathStorage.str()));
|
2017-04-27 07:15:10 +08:00
|
|
|
llvm::sys::path::native("//net/dir/file2", PathStorage);
|
2020-01-29 03:23:46 +08:00
|
|
|
expected_files.push_back(std::string(PathStorage.str()));
|
2019-03-08 17:42:04 +08:00
|
|
|
llvm::sys::path::native("//net/file1", PathStorage);
|
2020-01-29 03:23:46 +08:00
|
|
|
expected_files.push_back(std::string(PathStorage.str()));
|
2016-08-19 03:31:48 +08:00
|
|
|
EXPECT_EQ(expected_files,
|
|
|
|
getAllFiles("[{\"directory\":\"//net/dir\","
|
|
|
|
"\"command\":\"command\","
|
|
|
|
"\"file\":\"file1\"},"
|
|
|
|
" {\"directory\":\"//net/dir\","
|
|
|
|
"\"command\":\"command\","
|
2019-03-08 17:42:04 +08:00
|
|
|
"\"file\":\"../file1\"},"
|
|
|
|
" {\"directory\":\"//net/dir\","
|
|
|
|
"\"command\":\"command\","
|
2016-08-19 03:31:48 +08:00
|
|
|
"\"file\":\"file2\"}]",
|
|
|
|
ErrorMessage, JSONCommandLineSyntax::Gnu))
|
|
|
|
<< ErrorMessage;
|
2012-07-13 20:31:45 +08:00
|
|
|
}
|
|
|
|
|
2012-12-04 15:26:44 +08:00
|
|
|
TEST(JSONCompilationDatabase, GetAllCompileCommands) {
|
|
|
|
std::string ErrorMessage;
|
2016-08-19 03:31:48 +08:00
|
|
|
EXPECT_EQ(
|
|
|
|
0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage)
|
|
|
|
.size())
|
|
|
|
<< ErrorMessage;
|
2012-12-04 15:26:44 +08:00
|
|
|
|
|
|
|
StringRef Directory1("//net/dir1");
|
|
|
|
StringRef FileName1("file1");
|
|
|
|
StringRef Command1("command1");
|
2016-12-02 07:37:45 +08:00
|
|
|
StringRef Output1("file1.o");
|
2012-12-04 15:26:44 +08:00
|
|
|
StringRef Directory2("//net/dir2");
|
2015-09-17 02:28:42 +08:00
|
|
|
StringRef FileName2("file2");
|
|
|
|
StringRef Command2("command2");
|
2016-12-02 07:37:45 +08:00
|
|
|
StringRef Output2("");
|
2012-12-04 15:26:44 +08:00
|
|
|
|
|
|
|
std::vector<CompileCommand> Commands = getAllCompileCommands(
|
2016-08-19 03:31:48 +08:00
|
|
|
JSONCommandLineSyntax::Gnu,
|
|
|
|
("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 +
|
|
|
|
"\","
|
|
|
|
"\"file\":\"" +
|
2016-12-02 07:37:45 +08:00
|
|
|
FileName1 + "\", \"output\":\"" +
|
|
|
|
Output1 + "\"},"
|
2016-08-19 03:31:48 +08:00
|
|
|
" {\"directory\":\"" +
|
|
|
|
Directory2 + "\"," + "\"command\":\"" + Command2 + "\","
|
|
|
|
"\"file\":\"" +
|
|
|
|
FileName2 + "\"}]")
|
|
|
|
.str(),
|
2012-12-04 15:26:44 +08:00
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
|
|
|
|
EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage;
|
2015-09-17 02:28:42 +08:00
|
|
|
EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage;
|
2016-12-02 07:37:45 +08:00
|
|
|
EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage;
|
2012-12-04 15:26:44 +08:00
|
|
|
ASSERT_EQ(1u, Commands[0].CommandLine.size());
|
|
|
|
EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage;
|
|
|
|
EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage;
|
2015-09-17 02:28:42 +08:00
|
|
|
EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage;
|
2016-12-02 07:37:45 +08:00
|
|
|
EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage;
|
2012-12-04 15:26:44 +08:00
|
|
|
ASSERT_EQ(1u, Commands[1].CommandLine.size());
|
|
|
|
EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage;
|
2015-09-23 01:22:33 +08:00
|
|
|
|
|
|
|
// Check that order is preserved.
|
|
|
|
Commands = getAllCompileCommands(
|
2016-08-19 03:31:48 +08:00
|
|
|
JSONCommandLineSyntax::Gnu,
|
|
|
|
("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 +
|
|
|
|
"\","
|
|
|
|
"\"file\":\"" +
|
|
|
|
FileName2 + "\"},"
|
|
|
|
" {\"directory\":\"" +
|
|
|
|
Directory1 + "\"," + "\"command\":\"" + Command1 + "\","
|
|
|
|
"\"file\":\"" +
|
|
|
|
FileName1 + "\"}]")
|
|
|
|
.str(),
|
2015-09-23 01:22:33 +08:00
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
|
|
|
|
EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage;
|
|
|
|
EXPECT_EQ(FileName2, Commands[0].Filename) << ErrorMessage;
|
|
|
|
ASSERT_EQ(1u, Commands[0].CommandLine.size());
|
|
|
|
EXPECT_EQ(Command2, Commands[0].CommandLine[0]) << ErrorMessage;
|
|
|
|
EXPECT_EQ(Directory1, Commands[1].Directory) << ErrorMessage;
|
|
|
|
EXPECT_EQ(FileName1, Commands[1].Filename) << ErrorMessage;
|
|
|
|
ASSERT_EQ(1u, Commands[1].CommandLine.size());
|
|
|
|
EXPECT_EQ(Command1, Commands[1].CommandLine[0]) << ErrorMessage;
|
2012-12-04 15:26:44 +08:00
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
|
2020-12-04 18:34:30 +08:00
|
|
|
std::string JSONDatabase,
|
2012-04-04 20:07:46 +08:00
|
|
|
std::string &ErrorMessage) {
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<CompilationDatabase> Database(
|
2016-08-19 03:31:48 +08:00
|
|
|
JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
|
|
|
|
JSONCommandLineSyntax::Gnu));
|
2012-04-04 20:07:46 +08:00
|
|
|
if (!Database)
|
|
|
|
return CompileCommand();
|
2020-12-04 18:34:30 +08:00
|
|
|
// Overwrite the string to verify we're not reading from it later.
|
|
|
|
JSONDatabase.assign(JSONDatabase.size(), '*');
|
2012-04-04 20:07:46 +08:00
|
|
|
std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
|
|
|
|
EXPECT_LE(Commands.size(), 1u);
|
|
|
|
if (Commands.empty())
|
|
|
|
return CompileCommand();
|
|
|
|
return Commands[0];
|
|
|
|
}
|
|
|
|
|
2015-08-14 17:55:36 +08:00
|
|
|
TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) {
|
|
|
|
StringRef Directory("//net/dir");
|
|
|
|
StringRef FileName("//net/dir/filename");
|
|
|
|
StringRef Command("command");
|
|
|
|
StringRef Arguments = "arguments";
|
|
|
|
Twine ArgumentsAccumulate;
|
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
|
|
|
FileName,
|
|
|
|
("[{\"directory\":\"" + Directory + "\","
|
|
|
|
"\"arguments\":[\"" + Arguments + "\"],"
|
2015-09-08 23:14:06 +08:00
|
|
|
"\"command\":\"" + Command + "\","
|
2015-08-14 17:55:36 +08:00
|
|
|
"\"file\":\"" + FileName + "\"}]").str(),
|
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
|
|
|
|
EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
|
|
|
|
EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
2012-10-09 00:08:15 +08:00
|
|
|
struct FakeComparator : public PathComparator {
|
2015-10-20 21:23:58 +08:00
|
|
|
~FakeComparator() override {}
|
2015-04-11 10:00:23 +08:00
|
|
|
bool equivalent(StringRef FileA, StringRef FileB) const override {
|
2012-10-09 02:31:54 +08:00
|
|
|
return FileA.equals_lower(FileB);
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class FileMatchTrieTest : public ::testing::Test {
|
|
|
|
protected:
|
|
|
|
FileMatchTrieTest() : Trie(new FakeComparator()) {}
|
|
|
|
|
|
|
|
StringRef find(StringRef Path) {
|
|
|
|
llvm::raw_string_ostream ES(Error);
|
|
|
|
return Trie.findEquivalent(Path, ES);
|
|
|
|
}
|
|
|
|
|
|
|
|
FileMatchTrie Trie;
|
|
|
|
std::string Error;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, InsertingRelativePath) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/path/file.cc");
|
2012-10-09 00:08:15 +08:00
|
|
|
Trie.insert("file.cc");
|
2012-10-09 04:08:25 +08:00
|
|
|
EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, MatchingRelativePath) {
|
|
|
|
EXPECT_EQ("", find("file.cc"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, ReturnsBestResults) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/d/c/b.cc");
|
|
|
|
Trie.insert("//net/d/b/b.cc");
|
|
|
|
EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, HandlesSymlinks) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/AA/file.cc");
|
|
|
|
EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/Aa/file.cc");
|
|
|
|
Trie.insert("//net/aA/file.cc");
|
|
|
|
EXPECT_TRUE(find("//net/aa/file.cc").empty());
|
2012-10-09 00:08:15 +08:00
|
|
|
EXPECT_EQ("Path is ambiguous", Error);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/src/Aa/file.cc");
|
|
|
|
Trie.insert("//net/src/aA/file.cc");
|
|
|
|
Trie.insert("//net/SRC/aa/file.cc");
|
|
|
|
EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, EmptyTrie) {
|
2012-10-09 04:08:25 +08:00
|
|
|
EXPECT_TRUE(find("//net/some/path").empty());
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, NoResult) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/somepath/otherfile.cc");
|
|
|
|
Trie.insert("//net/otherpath/somefile.cc");
|
|
|
|
EXPECT_EQ("", find("//net/somepath/somefile.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, RootElementDifferent) {
|
2012-10-09 04:08:25 +08:00
|
|
|
Trie.insert("//net/path/file.cc");
|
|
|
|
Trie.insert("//net/otherpath/file.cc");
|
|
|
|
EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
|
2012-10-09 00:08:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
|
|
|
|
EXPECT_EQ("", find("relative-path.cc"));
|
|
|
|
EXPECT_EQ("Cannot resolve relative paths", Error);
|
|
|
|
}
|
|
|
|
|
2020-07-18 00:48:56 +08:00
|
|
|
TEST_F(FileMatchTrieTest, SingleFile) {
|
|
|
|
Trie.insert("/root/RootFile.cc");
|
|
|
|
EXPECT_EQ("", find("/root/rootfile.cc"));
|
|
|
|
// Add subpath to avoid `if (Children.empty())` special case
|
|
|
|
// which we hit at previous `find()`.
|
|
|
|
Trie.insert("/root/otherpath/OtherFile.cc");
|
|
|
|
EXPECT_EQ("", find("/root/rootfile.cc"));
|
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
|
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand NotFound = findCompileArgsInJsonDatabase(
|
|
|
|
"a-file.cpp", "", ErrorMessage);
|
|
|
|
EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
|
|
|
|
EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
|
2012-10-09 04:08:25 +08:00
|
|
|
StringRef Directory("//net/some/directory");
|
|
|
|
StringRef FileName("//net/path/to/a-file.cpp");
|
|
|
|
StringRef Command("//net/path/to/compiler and some arguments");
|
2012-04-04 20:07:46 +08:00
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
|
|
|
FileName,
|
|
|
|
("[{\"directory\":\"" + Directory + "\"," +
|
|
|
|
"\"command\":\"" + Command + "\","
|
|
|
|
"\"file\":\"" + FileName + "\"}]").str(),
|
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
|
|
|
|
ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
|
2012-10-09 04:08:25 +08:00
|
|
|
EXPECT_EQ("//net/path/to/compiler",
|
|
|
|
FoundCommand.CommandLine[0]) << ErrorMessage;
|
2012-04-04 20:07:46 +08:00
|
|
|
EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
|
|
|
|
EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
|
|
|
|
EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
|
|
|
|
|
|
|
|
CompileCommand NotFound = findCompileArgsInJsonDatabase(
|
|
|
|
"a-file.cpp",
|
|
|
|
("[{\"directory\":\"" + Directory + "\"," +
|
|
|
|
"\"command\":\"" + Command + "\","
|
|
|
|
"\"file\":\"" + FileName + "\"}]").str(),
|
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
|
|
|
|
EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
|
2012-10-09 04:08:25 +08:00
|
|
|
StringRef Directory("//net/some/directory");
|
|
|
|
StringRef FileName("//net/path/to/a-file.cpp");
|
|
|
|
StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
|
2012-04-04 20:07:46 +08:00
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
|
|
|
FileName,
|
|
|
|
("[{\"directory\":\"" + Directory + "\"," +
|
|
|
|
"\"command\":\"" + Command + "\","
|
|
|
|
"\"file\":\"" + FileName + "\"}]").str(),
|
|
|
|
ErrorMessage);
|
|
|
|
ASSERT_EQ(2u, FoundCommand.CommandLine.size());
|
2012-10-09 04:08:25 +08:00
|
|
|
EXPECT_EQ("//net/path to compiler",
|
|
|
|
FoundCommand.CommandLine[0]) << ErrorMessage;
|
2012-04-04 20:07:46 +08:00
|
|
|
EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
|
2012-10-09 04:08:25 +08:00
|
|
|
StringRef Directory("//net/some directory / with spaces");
|
|
|
|
StringRef FileName("//net/path/to/a-file.cpp");
|
2012-04-04 20:07:46 +08:00
|
|
|
StringRef Command("a command");
|
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
|
|
|
FileName,
|
|
|
|
("[{\"directory\":\"" + Directory + "\"," +
|
|
|
|
"\"command\":\"" + Command + "\","
|
|
|
|
"\"file\":\"" + FileName + "\"}]").str(),
|
|
|
|
ErrorMessage);
|
|
|
|
EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(findCompileArgsInJsonDatabase, FindsEntry) {
|
2012-10-09 04:08:25 +08:00
|
|
|
StringRef Directory("//net/directory");
|
2012-04-04 20:07:46 +08:00
|
|
|
StringRef FileName("file");
|
|
|
|
StringRef Command("command");
|
|
|
|
std::string JsonDatabase = "[";
|
|
|
|
for (int I = 0; I < 10; ++I) {
|
|
|
|
if (I > 0) JsonDatabase += ",";
|
|
|
|
JsonDatabase +=
|
|
|
|
("{\"directory\":\"" + Directory + Twine(I) + "\"," +
|
|
|
|
"\"command\":\"" + Command + Twine(I) + "\","
|
|
|
|
"\"file\":\"" + FileName + Twine(I) + "\"}").str();
|
|
|
|
}
|
|
|
|
JsonDatabase += "]";
|
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
2012-10-09 04:08:25 +08:00
|
|
|
"//net/directory4/file4", JsonDatabase, ErrorMessage);
|
|
|
|
EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage;
|
2012-04-04 20:07:46 +08:00
|
|
|
ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
|
|
|
|
EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
|
|
|
|
}
|
|
|
|
|
2019-07-13 07:38:31 +08:00
|
|
|
TEST(findCompileArgsInJsonDatabase, ParsesCompilerWrappers) {
|
|
|
|
std::vector<std::pair<std::string, std::string>> Cases = {
|
|
|
|
{"distcc gcc foo.c", "gcc foo.c"},
|
|
|
|
{"gomacc clang++ foo.c", "clang++ foo.c"},
|
2020-10-04 13:07:20 +08:00
|
|
|
{"sccache clang++ foo.c", "clang++ foo.c"},
|
2019-07-13 07:38:31 +08:00
|
|
|
{"ccache gcc foo.c", "gcc foo.c"},
|
|
|
|
{"ccache.exe gcc foo.c", "gcc foo.c"},
|
|
|
|
{"ccache g++.exe foo.c", "g++.exe foo.c"},
|
|
|
|
{"ccache distcc gcc foo.c", "gcc foo.c"},
|
|
|
|
|
|
|
|
{"distcc foo.c", "distcc foo.c"},
|
|
|
|
{"distcc -I/foo/bar foo.c", "distcc -I/foo/bar foo.c"},
|
|
|
|
};
|
|
|
|
std::string ErrorMessage;
|
|
|
|
|
|
|
|
for (const auto &Case : Cases) {
|
|
|
|
std::string DB =
|
|
|
|
R"([{"directory":"//net/dir", "file":"//net/dir/foo.c", "command":")" +
|
|
|
|
Case.first + "\"}]";
|
|
|
|
CompileCommand FoundCommand =
|
|
|
|
findCompileArgsInJsonDatabase("//net/dir/foo.c", DB, ErrorMessage);
|
|
|
|
EXPECT_EQ(Case.second, llvm::join(FoundCommand.CommandLine, " "))
|
|
|
|
<< Case.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
|
|
|
|
std::string JsonDatabase =
|
2012-10-09 04:08:25 +08:00
|
|
|
("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
|
2012-04-04 20:07:46 +08:00
|
|
|
Command + "\"}]").str();
|
|
|
|
std::string ErrorMessage;
|
|
|
|
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
|
2012-10-09 04:08:25 +08:00
|
|
|
"//net/root/test", JsonDatabase, ErrorMessage);
|
2012-04-04 20:07:46 +08:00
|
|
|
EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
|
|
|
|
return FoundCommand.CommandLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine("");
|
|
|
|
EXPECT_TRUE(Result.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
|
|
|
|
ASSERT_EQ(3ul, Result.size());
|
|
|
|
EXPECT_EQ("a", Result[0]);
|
|
|
|
EXPECT_EQ("b", Result[1]);
|
|
|
|
EXPECT_EQ("c", Result[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
|
|
|
|
ASSERT_EQ(2ul, Result.size());
|
|
|
|
EXPECT_EQ("a", Result[0]);
|
|
|
|
EXPECT_EQ("b", Result[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
|
|
|
|
std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
|
|
|
|
ASSERT_EQ(1ul, Backslash.size());
|
|
|
|
EXPECT_EQ("a\\", Backslash[0]);
|
|
|
|
std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
|
|
|
|
ASSERT_EQ(1ul, Quote.size());
|
|
|
|
EXPECT_EQ("a\"", Quote[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
EXPECT_EQ(" a b ", Result[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine(
|
|
|
|
" \\\" a \\\" \\\" b \\\" ");
|
|
|
|
ASSERT_EQ(2ul, Result.size());
|
|
|
|
EXPECT_EQ(" a ", Result[0]);
|
|
|
|
EXPECT_EQ(" b ", Result[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine(
|
|
|
|
"\\\"\\\"\\\"\\\"");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
EXPECT_TRUE(Result[0].empty()) << Result[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine(
|
|
|
|
"\\\"\\\\\\\"\\\"");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
EXPECT_EQ("\"", Result[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
|
|
|
|
std::vector<std::string> Result = unescapeJsonCommandLine(
|
|
|
|
" \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
|
|
|
|
ASSERT_EQ(4ul, Result.size());
|
|
|
|
EXPECT_EQ("\"", Result[0]);
|
|
|
|
EXPECT_EQ("a \" b ", Result[1]);
|
|
|
|
EXPECT_EQ("and\\c", Result[2]);
|
|
|
|
EXPECT_EQ("\"", Result[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
|
|
|
|
std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
|
|
|
|
"\\\"a\\\"\\\"b\\\"");
|
|
|
|
ASSERT_EQ(1ul, QuotedNoSpaces.size());
|
|
|
|
EXPECT_EQ("ab", QuotedNoSpaces[0]);
|
|
|
|
|
|
|
|
std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
|
|
|
|
"\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
|
|
|
|
ASSERT_EQ(1ul, MixedNoSpaces.size());
|
|
|
|
EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
|
|
|
|
std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
|
|
|
|
ASSERT_EQ(1ul, Unclosed.size());
|
|
|
|
EXPECT_EQ("abc", Unclosed[0]);
|
|
|
|
|
|
|
|
std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
|
|
|
|
ASSERT_EQ(1ul, Empty.size());
|
|
|
|
EXPECT_EQ("", Empty[0]);
|
|
|
|
}
|
|
|
|
|
2013-03-02 14:00:16 +08:00
|
|
|
TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
|
|
|
|
std::vector<std::string> Args = unescapeJsonCommandLine("a'\\\\b \\\"c\\\"'");
|
|
|
|
ASSERT_EQ(1ul, Args.size());
|
|
|
|
EXPECT_EQ("a\\b \"c\"", Args[0]);
|
|
|
|
}
|
|
|
|
|
2012-04-18 15:41:50 +08:00
|
|
|
TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
|
2019-01-16 17:41:26 +08:00
|
|
|
FixedCompilationDatabase Database(".", /*CommandLine*/ {"one", "two"});
|
2015-09-17 02:28:42 +08:00
|
|
|
StringRef FileName("source");
|
2012-04-18 15:41:50 +08:00
|
|
|
std::vector<CompileCommand> Result =
|
2015-09-17 02:28:42 +08:00
|
|
|
Database.getCompileCommands(FileName);
|
2012-04-18 15:41:50 +08:00
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
EXPECT_EQ(".", Result[0].Directory);
|
2015-09-17 02:28:42 +08:00
|
|
|
EXPECT_EQ(FileName, Result[0].Filename);
|
2019-01-16 17:41:26 +08:00
|
|
|
EXPECT_THAT(Result[0].CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "one", "two", "source"));
|
2012-04-18 15:41:50 +08:00
|
|
|
}
|
|
|
|
|
2012-07-13 20:31:45 +08:00
|
|
|
TEST(FixedCompilationDatabase, GetAllFiles) {
|
|
|
|
std::vector<std::string> CommandLine;
|
|
|
|
CommandLine.push_back("one");
|
|
|
|
CommandLine.push_back("two");
|
|
|
|
FixedCompilationDatabase Database(".", CommandLine);
|
|
|
|
|
|
|
|
EXPECT_EQ(0ul, Database.getAllFiles().size());
|
|
|
|
}
|
|
|
|
|
2012-12-04 15:26:44 +08:00
|
|
|
TEST(FixedCompilationDatabase, GetAllCompileCommands) {
|
|
|
|
std::vector<std::string> CommandLine;
|
|
|
|
CommandLine.push_back("one");
|
|
|
|
CommandLine.push_back("two");
|
|
|
|
FixedCompilationDatabase Database(".", CommandLine);
|
|
|
|
|
|
|
|
EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
|
|
|
|
}
|
|
|
|
|
2020-12-04 16:58:50 +08:00
|
|
|
TEST(FixedCompilationDatabase, FromBuffer) {
|
|
|
|
const char *Data = R"(
|
|
|
|
|
|
|
|
-DFOO=BAR
|
|
|
|
|
|
|
|
--baz
|
|
|
|
|
|
|
|
)";
|
|
|
|
std::string ErrorMsg;
|
|
|
|
auto CDB =
|
|
|
|
FixedCompilationDatabase::loadFromBuffer("/cdb/dir", Data, ErrorMsg);
|
|
|
|
|
|
|
|
std::vector<CompileCommand> Result = CDB->getCompileCommands("/foo/bar.cc");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
EXPECT_EQ("/cdb/dir", Result.front().Directory);
|
|
|
|
EXPECT_EQ("/foo/bar.cc", Result.front().Filename);
|
|
|
|
EXPECT_THAT(
|
|
|
|
Result.front().CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "-DFOO=BAR", "--baz", "/foo/bar.cc"));
|
|
|
|
}
|
|
|
|
|
2012-04-18 15:41:50 +08:00
|
|
|
TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
|
|
|
|
int Argc = 0;
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database =
|
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, nullptr, ErrorMsg);
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_FALSE(Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
EXPECT_TRUE(ErrorMsg.empty());
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_EQ(0, Argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
|
|
|
|
int Argc = 2;
|
|
|
|
const char *Argv[] = { "1", "2" };
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database(
|
2017-05-24 19:57:37 +08:00
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_FALSE(Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
EXPECT_TRUE(ErrorMsg.empty());
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_EQ(2, Argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
|
|
|
|
int Argc = 5;
|
2013-11-18 00:08:04 +08:00
|
|
|
const char *Argv[] = {
|
|
|
|
"1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
|
|
|
|
};
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database(
|
2017-05-24 19:57:37 +08:00
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
|
2014-03-08 03:51:06 +08:00
|
|
|
ASSERT_TRUE((bool)Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
ASSERT_TRUE(ErrorMsg.empty());
|
2012-04-18 15:41:50 +08:00
|
|
|
std::vector<CompileCommand> Result =
|
|
|
|
Database->getCompileCommands("source");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
ASSERT_EQ(".", Result[0].Directory);
|
2019-01-16 17:41:26 +08:00
|
|
|
ASSERT_THAT(Result[0].CommandLine, ElementsAre(EndsWith("clang-tool"),
|
|
|
|
"-DDEF3", "-DDEF4", "source"));
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_EQ(2, Argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
|
|
|
|
int Argc = 3;
|
|
|
|
const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database =
|
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
|
2014-03-08 03:51:06 +08:00
|
|
|
ASSERT_TRUE((bool)Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
ASSERT_TRUE(ErrorMsg.empty());
|
2012-04-18 15:41:50 +08:00
|
|
|
std::vector<CompileCommand> Result =
|
|
|
|
Database->getCompileCommands("source");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
ASSERT_EQ(".", Result[0].Directory);
|
2019-01-16 17:41:26 +08:00
|
|
|
ASSERT_THAT(Result[0].CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "source"));
|
2012-04-18 15:41:50 +08:00
|
|
|
EXPECT_EQ(2, Argc);
|
|
|
|
}
|
|
|
|
|
2013-11-18 00:08:04 +08:00
|
|
|
TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
|
|
|
|
const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
|
|
|
|
int Argc = sizeof(Argv) / sizeof(char*);
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database =
|
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
|
2014-03-08 03:51:06 +08:00
|
|
|
ASSERT_TRUE((bool)Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
ASSERT_TRUE(ErrorMsg.empty());
|
2013-11-18 00:08:04 +08:00
|
|
|
std::vector<CompileCommand> Result =
|
|
|
|
Database->getCompileCommands("source");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
ASSERT_EQ(".", Result[0].Directory);
|
2019-01-16 17:41:26 +08:00
|
|
|
ASSERT_THAT(Result[0].CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "-c", "-DDEF3", "source"));
|
2013-11-18 00:08:04 +08:00
|
|
|
EXPECT_EQ(2, Argc);
|
|
|
|
}
|
|
|
|
|
2017-06-29 18:43:44 +08:00
|
|
|
TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
|
|
|
|
// Adjust the given command line arguments to ensure that any positional
|
|
|
|
// arguments in them are stripped.
|
|
|
|
const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
|
|
|
|
int Argc = llvm::array_lengthof(Argv);
|
|
|
|
std::string ErrorMessage;
|
|
|
|
std::unique_ptr<CompilationDatabase> Database =
|
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMessage);
|
|
|
|
ASSERT_TRUE((bool)Database);
|
|
|
|
ASSERT_TRUE(ErrorMessage.empty());
|
|
|
|
std::vector<CompileCommand> Result = Database->getCompileCommands("source");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
ASSERT_EQ(".", Result[0].Directory);
|
2019-01-16 17:41:26 +08:00
|
|
|
ASSERT_THAT(
|
|
|
|
Result[0].CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "-fsyntax-only", "-DDEF3", "source"));
|
2017-06-29 18:43:44 +08:00
|
|
|
}
|
|
|
|
|
2013-11-18 00:08:04 +08:00
|
|
|
TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
|
|
|
|
const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
|
|
|
|
int Argc = sizeof(Argv) / sizeof(char*);
|
2017-05-24 19:57:37 +08:00
|
|
|
std::string ErrorMsg;
|
|
|
|
std::unique_ptr<FixedCompilationDatabase> Database =
|
|
|
|
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
|
2014-03-08 03:51:06 +08:00
|
|
|
ASSERT_TRUE((bool)Database);
|
2017-05-24 19:57:37 +08:00
|
|
|
ASSERT_TRUE(ErrorMsg.empty());
|
2013-11-18 00:08:04 +08:00
|
|
|
std::vector<CompileCommand> Result =
|
|
|
|
Database->getCompileCommands("source");
|
|
|
|
ASSERT_EQ(1ul, Result.size());
|
|
|
|
ASSERT_EQ(".", Result[0].Directory);
|
|
|
|
std::vector<std::string> Expected;
|
2019-01-16 17:41:26 +08:00
|
|
|
ASSERT_THAT(Result[0].CommandLine,
|
|
|
|
ElementsAre(EndsWith("clang-tool"), "source"));
|
2013-11-18 00:08:04 +08:00
|
|
|
EXPECT_EQ(2, Argc);
|
|
|
|
}
|
|
|
|
|
2018-04-09 23:17:39 +08:00
|
|
|
struct MemCDB : public CompilationDatabase {
|
|
|
|
using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>;
|
|
|
|
EntryMap Entries;
|
|
|
|
MemCDB(const EntryMap &E) : Entries(E) {}
|
|
|
|
|
|
|
|
std::vector<CompileCommand> getCompileCommands(StringRef F) const override {
|
|
|
|
auto Ret = Entries.lookup(F);
|
|
|
|
return {Ret.begin(), Ret.end()};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> getAllFiles() const override {
|
|
|
|
std::vector<std::string> Result;
|
|
|
|
for (const auto &Entry : Entries)
|
2020-01-29 03:23:46 +08:00
|
|
|
Result.push_back(std::string(Entry.first()));
|
2018-04-09 23:17:39 +08:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-26 15:39:03 +08:00
|
|
|
class MemDBTest : public ::testing::Test {
|
2018-04-09 23:17:39 +08:00
|
|
|
protected:
|
|
|
|
// Adds an entry to the underlying compilation database.
|
|
|
|
// A flag is injected: -D <File>, so the command used can be identified.
|
2018-09-09 20:06:35 +08:00
|
|
|
void add(StringRef File, StringRef Clang, StringRef Flags) {
|
|
|
|
SmallVector<StringRef, 8> Argv = {Clang, File, "-D", File};
|
2018-04-09 23:17:39 +08:00
|
|
|
llvm::SplitString(Flags, Argv);
|
2018-09-09 20:06:35 +08:00
|
|
|
|
|
|
|
SmallString<32> Dir;
|
2018-04-09 23:17:39 +08:00
|
|
|
llvm::sys::path::system_temp_directory(false, Dir);
|
2018-09-09 20:06:35 +08:00
|
|
|
|
2018-04-09 23:17:39 +08:00
|
|
|
Entries[path(File)].push_back(
|
|
|
|
{Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
|
|
|
|
}
|
2018-09-09 20:06:35 +08:00
|
|
|
void add(StringRef File, StringRef Flags = "") { add(File, "clang", Flags); }
|
2018-04-09 23:17:39 +08:00
|
|
|
|
|
|
|
// Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h)
|
|
|
|
std::string path(llvm::SmallString<32> File) {
|
|
|
|
llvm::SmallString<32> Dir;
|
|
|
|
llvm::sys::path::system_temp_directory(false, Dir);
|
|
|
|
llvm::sys::path::native(File);
|
|
|
|
llvm::SmallString<64> Result;
|
|
|
|
llvm::sys::path::append(Result, Dir, File);
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(Result.str());
|
2018-04-09 23:17:39 +08:00
|
|
|
}
|
|
|
|
|
2019-06-26 15:39:03 +08:00
|
|
|
MemCDB::EntryMap Entries;
|
|
|
|
};
|
|
|
|
|
|
|
|
class InterpolateTest : public MemDBTest {
|
|
|
|
protected:
|
2018-04-09 23:17:39 +08:00
|
|
|
// Look up the command from a relative path, and return it in string form.
|
|
|
|
// The input file is not included in the returned command.
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
std::string getCommand(llvm::StringRef F, bool MakeNative = true) {
|
2018-04-09 23:17:39 +08:00
|
|
|
auto Results =
|
2019-08-15 07:04:18 +08:00
|
|
|
inferMissingCompileCommands(std::make_unique<MemCDB>(Entries))
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
->getCompileCommands(MakeNative ? path(F) : F);
|
2018-04-09 23:17:39 +08:00
|
|
|
if (Results.empty())
|
|
|
|
return "none";
|
|
|
|
// drop the input file argument, so tests don't have to deal with path().
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
EXPECT_EQ(Results[0].CommandLine.back(), MakeNative ? path(F) : F)
|
2018-04-09 23:17:39 +08:00
|
|
|
<< "Last arg should be the file";
|
|
|
|
Results[0].CommandLine.pop_back();
|
|
|
|
return llvm::join(Results[0].CommandLine, " ");
|
|
|
|
}
|
|
|
|
|
2019-04-05 23:22:20 +08:00
|
|
|
// Parse the file whose command was used out of the Heuristic string.
|
|
|
|
std::string getProxy(llvm::StringRef F) {
|
|
|
|
auto Results =
|
2019-08-15 07:04:18 +08:00
|
|
|
inferMissingCompileCommands(std::make_unique<MemCDB>(Entries))
|
2019-04-05 23:22:20 +08:00
|
|
|
->getCompileCommands(path(F));
|
|
|
|
if (Results.empty())
|
|
|
|
return "none";
|
|
|
|
StringRef Proxy = Results.front().Heuristic;
|
|
|
|
if (!Proxy.consume_front("inferred from "))
|
|
|
|
return "";
|
|
|
|
// We have a proxy file, convert back to a unix relative path.
|
|
|
|
// This is a bit messy, but we do need to test these strings somehow...
|
|
|
|
llvm::SmallString<32> TempDir;
|
|
|
|
llvm::sys::path::system_temp_directory(false, TempDir);
|
|
|
|
Proxy.consume_front(TempDir);
|
|
|
|
Proxy.consume_front(llvm::sys::path::get_separator());
|
|
|
|
llvm::SmallString<32> Result = Proxy;
|
|
|
|
llvm::sys::path::native(Result, llvm::sys::path::Style::posix);
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(Result.str());
|
2019-04-05 23:22:20 +08:00
|
|
|
}
|
2018-04-09 23:17:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, Nearby) {
|
|
|
|
add("dir/foo.cpp");
|
|
|
|
add("dir/bar.cpp");
|
|
|
|
add("an/other/foo.cpp");
|
|
|
|
|
|
|
|
// great: dir and name both match (prefix or full, case insensitive)
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp");
|
|
|
|
EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp");
|
2018-04-09 23:17:39 +08:00
|
|
|
// no name match. prefer matching dir, break ties by alpha
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp");
|
2018-04-09 23:17:39 +08:00
|
|
|
// an exact name match beats one segment of directory match
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp");
|
2018-04-09 23:17:39 +08:00
|
|
|
// two segments of directory match beat a prefix name match
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp");
|
2018-04-09 23:17:39 +08:00
|
|
|
// if nothing matches at all, we still get the closest alpha match
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp");
|
2018-04-09 23:17:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, Language) {
|
|
|
|
add("dir/foo.cpp", "-std=c++17");
|
2018-08-29 00:15:56 +08:00
|
|
|
add("dir/bar.c", "");
|
2018-04-09 23:17:39 +08:00
|
|
|
add("dir/baz.cee", "-x c");
|
2019-12-20 05:22:01 +08:00
|
|
|
add("dir/aux.cpp", "-std=c++17 -x objective-c++");
|
2018-04-09 23:17:39 +08:00
|
|
|
|
|
|
|
// .h is ambiguous, so we add explicit language flags
|
|
|
|
EXPECT_EQ(getCommand("foo.h"),
|
2018-04-11 17:18:18 +08:00
|
|
|
"clang -D dir/foo.cpp -x c++-header -std=c++17");
|
2019-05-07 22:34:06 +08:00
|
|
|
// Same thing if we have no extension. (again, we treat as header).
|
|
|
|
EXPECT_EQ(getCommand("foo"), "clang -D dir/foo.cpp -x c++-header -std=c++17");
|
|
|
|
// and invalid extensions.
|
|
|
|
EXPECT_EQ(getCommand("foo.cce"),
|
|
|
|
"clang -D dir/foo.cpp -x c++-header -std=c++17");
|
2018-04-09 23:17:39 +08:00
|
|
|
// and don't add -x if the inferred language is correct.
|
2018-04-11 17:18:18 +08:00
|
|
|
EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
|
2018-04-09 23:17:39 +08:00
|
|
|
// respect -x if it's already there.
|
|
|
|
EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
|
2018-08-29 00:15:56 +08:00
|
|
|
// prefer a worse match with the right extension.
|
|
|
|
EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c");
|
|
|
|
Entries.erase(path(StringRef("dir/bar.c")));
|
2018-04-09 23:17:39 +08:00
|
|
|
// Now we transfer across languages, so drop -std too.
|
|
|
|
EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
|
2019-12-20 05:22:01 +08:00
|
|
|
// Prefer -x over -std when overriding language.
|
|
|
|
EXPECT_EQ(getCommand("aux.h"),
|
|
|
|
"clang -D dir/aux.cpp -x objective-c++-header -std=c++17");
|
2018-04-09 23:17:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, Strip) {
|
|
|
|
add("dir/foo.cpp", "-o foo.o -Wall");
|
|
|
|
// the -o option and the input file are removed, but -Wall is preserved.
|
|
|
|
EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
|
|
|
|
}
|
|
|
|
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
TEST_F(InterpolateTest, StripDoubleDash) {
|
|
|
|
add("dir/foo.cpp", "-o foo.o -std=c++14 -Wall -- dir/foo.cpp");
|
|
|
|
// input file and output option are removed
|
|
|
|
// -Wall flag isn't
|
|
|
|
// -std option gets re-added as the last argument before the input file
|
|
|
|
// -- is removed as it's not necessary - the new input file doesn't start with
|
|
|
|
// a dash
|
|
|
|
EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall -std=c++14");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, InsertDoubleDash) {
|
|
|
|
add("dir/foo.cpp", "-o foo.o -std=c++14 -Wall");
|
|
|
|
EXPECT_EQ(getCommand("-dir/bar.cpp", false),
|
|
|
|
"clang -D dir/foo.cpp -Wall -std=c++14 --");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, InsertDoubleDashForClangCL) {
|
|
|
|
add("dir/foo.cpp", "clang-cl", "/std:c++14 /W4");
|
|
|
|
EXPECT_EQ(getCommand("/dir/bar.cpp", false),
|
|
|
|
"clang-cl -D dir/foo.cpp /W4 /std:c++14 --");
|
|
|
|
}
|
|
|
|
|
2018-04-09 23:17:39 +08:00
|
|
|
TEST_F(InterpolateTest, Case) {
|
|
|
|
add("FOO/BAR/BAZ/SHOUT.cc");
|
|
|
|
add("foo/bar/baz/quiet.cc");
|
|
|
|
// Case mismatches are completely ignored, so we choose the name match.
|
2019-04-05 23:22:20 +08:00
|
|
|
EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc");
|
2018-04-09 23:17:39 +08:00
|
|
|
}
|
|
|
|
|
2018-09-09 20:06:35 +08:00
|
|
|
TEST_F(InterpolateTest, Aliasing) {
|
|
|
|
add("foo.cpp", "-faligned-new");
|
|
|
|
|
|
|
|
// The interpolated command should keep the given flag as written, even though
|
|
|
|
// the flag is internally represented as an alias.
|
|
|
|
EXPECT_EQ(getCommand("foo.hpp"), "clang -D foo.cpp -faligned-new");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, ClangCL) {
|
|
|
|
add("foo.cpp", "clang-cl", "/W4");
|
|
|
|
|
|
|
|
// Language flags should be added with CL syntax.
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
EXPECT_EQ(getCommand("foo.h", false), "clang-cl -D foo.cpp /W4 /TP");
|
2018-09-09 20:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InterpolateTest, DriverModes) {
|
|
|
|
add("foo.cpp", "clang-cl", "--driver-mode=gcc");
|
|
|
|
add("bar.cpp", "clang", "--driver-mode=cl");
|
|
|
|
|
|
|
|
// --driver-mode overrides should be respected.
|
[Tooling] Handle compilation databases containing commands with double dashes
As of CMake commit https://gitlab.kitware.com/cmake/cmake/-/commit/d993ebd4,
which first appeared in CMake 3.19.x series, in the compile commands for
clang-cl, CMake puts `--` before the input file. When operating on such a
database, the `InterpolatingCompilationDatabase` - specifically, the
`TransferableCommand` constructor - does not recognize that pattern and so, does
not strip the input, or the double dash when 'transferring' the compile command.
This results in a incorrect compile command - with the double dash and old input
file left in, and the language options and new input file appended after them,
where they're all treated as inputs, including the language version option.
Test files for some tests have names similar enough to be matched to commands
from the database, e.g.:
`.../path-mappings.test.tmp/server/bar.cpp`
can be matched to:
`.../Driver/ToolChains/BareMetal.cpp`
etc. When that happens, the tool being tested tries to use the matched, and
incorrectly 'transferred' compile command, and fails, reporting errors similar
to:
`error: no such file or directory: '/std:c++14'; did you mean '/std:c++14'? [clang-diagnostic-error]`
This happens in at least 4 tests:
Clang Tools :: clang-tidy/checkers/performance-trivially-destructible.cpp
Clangd :: check-fail.test
Clangd :: check.test
Clangd :: path-mappings.test
The fix for `TransferableCommand` removes the `--` and everything after it when
determining the arguments that apply to the new file. `--` is inserted in the
'transferred' command if the new file name starts with `-` and when operating in
clang-cl mode, also `/`. Additionally, other places in the code known to do
argument adjustment without accounting for the `--` and causing the tests to
fail are fixed as well.
Differential Revision: https://reviews.llvm.org/D98824
2021-03-25 04:01:08 +08:00
|
|
|
EXPECT_EQ(getCommand("foo.h"),
|
|
|
|
"clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
|
|
|
|
EXPECT_EQ(getCommand("bar.h", false),
|
|
|
|
"clang -D bar.cpp --driver-mode=cl /TP");
|
2018-09-09 20:06:35 +08:00
|
|
|
}
|
|
|
|
|
2021-02-24 09:43:53 +08:00
|
|
|
TEST(TransferCompileCommandTest, Smoke) {
|
|
|
|
CompileCommand Cmd;
|
|
|
|
Cmd.Filename = "foo.cc";
|
|
|
|
Cmd.CommandLine = {"clang", "-Wall", "foo.cc"};
|
|
|
|
Cmd.Directory = "dir";
|
|
|
|
CompileCommand Transferred = transferCompileCommand(std::move(Cmd), "foo.h");
|
|
|
|
EXPECT_EQ(Transferred.Filename, "foo.h");
|
|
|
|
EXPECT_THAT(Transferred.CommandLine,
|
|
|
|
ElementsAre("clang", "-Wall", "-x", "c++-header", "foo.h"));
|
|
|
|
EXPECT_EQ(Transferred.Directory, "dir");
|
|
|
|
}
|
|
|
|
|
2018-07-17 22:13:05 +08:00
|
|
|
TEST(CompileCommandTest, EqualityOperator) {
|
|
|
|
CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o");
|
|
|
|
CompileCommand CCTest = CCRef;
|
|
|
|
|
|
|
|
EXPECT_TRUE(CCRef == CCTest);
|
|
|
|
EXPECT_FALSE(CCRef != CCTest);
|
|
|
|
|
|
|
|
CCTest = CCRef;
|
|
|
|
CCTest.Directory = "/foo/baz";
|
|
|
|
EXPECT_FALSE(CCRef == CCTest);
|
|
|
|
EXPECT_TRUE(CCRef != CCTest);
|
|
|
|
|
|
|
|
CCTest = CCRef;
|
|
|
|
CCTest.Filename = "bonjour.c";
|
|
|
|
EXPECT_FALSE(CCRef == CCTest);
|
|
|
|
EXPECT_TRUE(CCRef != CCTest);
|
|
|
|
|
|
|
|
CCTest = CCRef;
|
|
|
|
CCTest.CommandLine.push_back("c");
|
|
|
|
EXPECT_FALSE(CCRef == CCTest);
|
|
|
|
EXPECT_TRUE(CCRef != CCTest);
|
|
|
|
|
|
|
|
CCTest = CCRef;
|
|
|
|
CCTest.Output = "bonjour.o";
|
|
|
|
EXPECT_FALSE(CCRef == CCTest);
|
|
|
|
EXPECT_TRUE(CCRef != CCTest);
|
|
|
|
}
|
|
|
|
|
2019-06-26 15:39:03 +08:00
|
|
|
class TargetAndModeTest : public MemDBTest {
|
|
|
|
public:
|
|
|
|
TargetAndModeTest() { llvm::InitializeAllTargetInfos(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
// Look up the command from a relative path, and return it in string form.
|
|
|
|
std::string getCommand(llvm::StringRef F) {
|
2019-08-15 07:04:18 +08:00
|
|
|
auto Results = inferTargetAndDriverMode(std::make_unique<MemCDB>(Entries))
|
2019-06-26 15:39:03 +08:00
|
|
|
->getCompileCommands(path(F));
|
|
|
|
if (Results.empty())
|
|
|
|
return "none";
|
|
|
|
return llvm::join(Results[0].CommandLine, " ");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(TargetAndModeTest, TargetAndMode) {
|
|
|
|
add("foo.cpp", "clang-cl", "");
|
2019-06-26 16:39:42 +08:00
|
|
|
add("bar.cpp", "clang++", "");
|
2019-06-26 15:39:03 +08:00
|
|
|
|
|
|
|
EXPECT_EQ(getCommand("foo.cpp"),
|
|
|
|
"clang-cl --driver-mode=cl foo.cpp -D foo.cpp");
|
|
|
|
EXPECT_EQ(getCommand("bar.cpp"),
|
2019-06-26 16:39:42 +08:00
|
|
|
"clang++ --driver-mode=g++ bar.cpp -D bar.cpp");
|
2019-06-26 15:39:03 +08:00
|
|
|
}
|
|
|
|
|
[clang][Tooling] Add support for .rsp files in compile_commands.json
Summary:
Add support for .rsp files.
Fixes https://github.com/clangd/clangd/issues/81
Patch By: liu hui(@lh123)
Reviewers: sammccall, ilya-biryukov, hokein, kadircet
Reviewed By: kadircet
Subscribers: merge_guards_bot, mgorny, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D70222
2019-11-29 19:14:25 +08:00
|
|
|
class ExpandResponseFilesTest : public MemDBTest {
|
|
|
|
public:
|
|
|
|
ExpandResponseFilesTest() : FS(new llvm::vfs::InMemoryFileSystem) {}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void addFile(StringRef File, StringRef Content) {
|
|
|
|
ASSERT_TRUE(
|
|
|
|
FS->addFile(File, 0, llvm::MemoryBuffer::getMemBufferCopy(Content)));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getCommand(llvm::StringRef F) {
|
|
|
|
auto Results = expandResponseFiles(std::make_unique<MemCDB>(Entries), FS)
|
|
|
|
->getCompileCommands(path(F));
|
|
|
|
if (Results.empty())
|
|
|
|
return "none";
|
|
|
|
return llvm::join(Results[0].CommandLine, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(ExpandResponseFilesTest, ExpandResponseFiles) {
|
|
|
|
addFile(path(StringRef("rsp1.rsp")), "-Dflag");
|
|
|
|
|
|
|
|
add("foo.cpp", "clang", "@rsp1.rsp");
|
|
|
|
add("bar.cpp", "clang", "-Dflag");
|
|
|
|
EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag");
|
|
|
|
EXPECT_EQ(getCommand("bar.cpp"), "clang bar.cpp -D bar.cpp -Dflag");
|
|
|
|
}
|
|
|
|
|
2012-04-04 20:07:46 +08:00
|
|
|
} // end namespace tooling
|
|
|
|
} // end namespace clang
|