summaryrefslogtreecommitdiff
path: root/unittests/Tooling/CompilationDatabaseTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Tooling/CompilationDatabaseTest.cpp')
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp138
1 files changed, 138 insertions, 0 deletions
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index fd8afe6b79762..ffc1d5b3a4acb 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -626,5 +626,143 @@ TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
EXPECT_EQ(2, Argc);
}
+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)
+ Result.push_back(Entry.first());
+ return Result;
+ }
+};
+
+class InterpolateTest : public ::testing::Test {
+protected:
+ // Adds an entry to the underlying compilation database.
+ // A flag is injected: -D <File>, so the command used can be identified.
+ void add(llvm::StringRef File, llvm::StringRef Flags = "") {
+ llvm::SmallVector<StringRef, 8> Argv = {"clang", File, "-D", File};
+ llvm::SplitString(Flags, Argv);
+ llvm::SmallString<32> Dir;
+ llvm::sys::path::system_temp_directory(false, Dir);
+ Entries[path(File)].push_back(
+ {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
+ }
+
+ // 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);
+ return Result.str();
+ }
+
+ // Look up the command from a relative path, and return it in string form.
+ // The input file is not included in the returned command.
+ std::string getCommand(llvm::StringRef F) {
+ auto Results =
+ inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries))
+ ->getCompileCommands(path(F));
+ if (Results.empty())
+ return "none";
+ // drop the input file argument, so tests don't have to deal with path().
+ EXPECT_EQ(Results[0].CommandLine.back(), path(F))
+ << "Last arg should be the file";
+ Results[0].CommandLine.pop_back();
+ return llvm::join(Results[0].CommandLine, " ");
+ }
+
+ MemCDB::EntryMap Entries;
+};
+
+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)
+ EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp");
+ EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp");
+ // no name match. prefer matching dir, break ties by alpha
+ EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp");
+ // an exact name match beats one segment of directory match
+ EXPECT_EQ(getCommand("some/other/bar.h"),
+ "clang -D dir/bar.cpp -x c++-header");
+ // two segments of directory match beat a prefix name match
+ EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp");
+ // if nothing matches at all, we still get the closest alpha match
+ EXPECT_EQ(getCommand("below/some/obscure/path.cpp"),
+ "clang -D an/other/foo.cpp");
+}
+
+TEST_F(InterpolateTest, Language) {
+ add("dir/foo.cpp", "-std=c++17");
+ add("dir/baz.cee", "-x c");
+
+ // .h is ambiguous, so we add explicit language flags
+ EXPECT_EQ(getCommand("foo.h"),
+ "clang -D dir/foo.cpp -x c++-header -std=c++17");
+ // and don't add -x if the inferred language is correct.
+ EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
+ // respect -x if it's already there.
+ EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
+ // prefer a worse match with the right language
+ EXPECT_EQ(getCommand("foo.c"), "clang -D dir/baz.cee");
+ Entries.erase(path(StringRef("dir/baz.cee")));
+ // Now we transfer across languages, so drop -std too.
+ EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
+}
+
+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");
+}
+
+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.
+ EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc");
+}
+
+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);
+}
+
} // end namespace tooling
} // end namespace clang