208 lines
7.8 KiB
C++
208 lines
7.8 KiB
C++
|
//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "clang/AST/ASTConsumer.h"
|
||
|
#include "clang/AST/DeclCXX.h"
|
||
|
#include "clang/AST/DeclGroup.h"
|
||
|
#include "clang/Frontend/ASTUnit.h"
|
||
|
#include "clang/Frontend/CompilerInstance.h"
|
||
|
#include "clang/Frontend/FrontendAction.h"
|
||
|
#include "clang/Frontend/FrontendActions.h"
|
||
|
#include "clang/Tooling/CompilationDatabase.h"
|
||
|
#include "clang/Tooling/Tooling.h"
|
||
|
#include "llvm/ADT/STLExtras.h"
|
||
|
#include "llvm/Support/FormatVariadic.h"
|
||
|
#include "llvm/Support/Path.h"
|
||
|
#include "llvm/Support/TargetRegistry.h"
|
||
|
#include "llvm/Support/TargetSelect.h"
|
||
|
#include "gtest/gtest.h"
|
||
|
#include <algorithm>
|
||
|
#include <string>
|
||
|
|
||
|
namespace clang {
|
||
|
namespace tooling {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
/// Prints out all of the gathered dependencies into a string.
|
||
|
class TestFileCollector : public DependencyFileGenerator {
|
||
|
public:
|
||
|
TestFileCollector(DependencyOutputOptions &Opts,
|
||
|
std::vector<std::string> &Deps)
|
||
|
: DependencyFileGenerator(Opts), Deps(Deps) {}
|
||
|
|
||
|
void finishedMainFile(DiagnosticsEngine &Diags) override {
|
||
|
auto NewDeps = getDependencies();
|
||
|
Deps.insert(Deps.end(), NewDeps.begin(), NewDeps.end());
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::vector<std::string> &Deps;
|
||
|
};
|
||
|
|
||
|
class TestDependencyScanningAction : public tooling::ToolAction {
|
||
|
public:
|
||
|
TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}
|
||
|
|
||
|
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
|
||
|
FileManager *FileMgr,
|
||
|
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
|
||
|
DiagnosticConsumer *DiagConsumer) override {
|
||
|
CompilerInstance Compiler(std::move(PCHContainerOps));
|
||
|
Compiler.setInvocation(std::move(Invocation));
|
||
|
Compiler.setFileManager(FileMgr);
|
||
|
|
||
|
Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
|
||
|
if (!Compiler.hasDiagnostics())
|
||
|
return false;
|
||
|
|
||
|
Compiler.createSourceManager(*FileMgr);
|
||
|
Compiler.addDependencyCollector(std::make_shared<TestFileCollector>(
|
||
|
Compiler.getInvocation().getDependencyOutputOpts(), Deps));
|
||
|
|
||
|
auto Action = std::make_unique<PreprocessOnlyAction>();
|
||
|
return Compiler.ExecuteAction(*Action);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::vector<std::string> &Deps;
|
||
|
};
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
TEST(DependencyScanner, ScanDepsReuseFilemanager) {
|
||
|
std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
|
||
|
StringRef CWD = "/root";
|
||
|
FixedCompilationDatabase CDB(CWD, Compilation);
|
||
|
|
||
|
auto VFS = new llvm::vfs::InMemoryFileSystem();
|
||
|
VFS->setCurrentWorkingDirectory(CWD);
|
||
|
auto Sept = llvm::sys::path::get_separator();
|
||
|
std::string HeaderPath =
|
||
|
std::string(llvm::formatv("{0}root{0}header.h", Sept));
|
||
|
std::string SymlinkPath =
|
||
|
std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
|
||
|
std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
|
||
|
|
||
|
VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
|
||
|
VFS->addHardLink(SymlinkPath, HeaderPath);
|
||
|
VFS->addFile(TestPath, 0,
|
||
|
llvm::MemoryBuffer::getMemBuffer(
|
||
|
"#include \"symlink.h\"\n#include \"header.h\"\n"));
|
||
|
|
||
|
ClangTool Tool(CDB, {"test.cpp"}, std::make_shared<PCHContainerOperations>(),
|
||
|
VFS);
|
||
|
Tool.clearArgumentsAdjusters();
|
||
|
std::vector<std::string> Deps;
|
||
|
TestDependencyScanningAction Action(Deps);
|
||
|
Tool.run(&Action);
|
||
|
using llvm::sys::path::convert_to_slash;
|
||
|
// The first invocation should return dependencies in order of access.
|
||
|
ASSERT_EQ(Deps.size(), 3u);
|
||
|
EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
|
||
|
|
||
|
// The file manager should still have two FileEntries, as one file is a
|
||
|
// hardlink.
|
||
|
FileManager &Files = Tool.getFiles();
|
||
|
EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
|
||
|
|
||
|
Deps.clear();
|
||
|
Tool.run(&Action);
|
||
|
// The second invocation should have the same order of dependencies.
|
||
|
ASSERT_EQ(Deps.size(), 3u);
|
||
|
EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
|
||
|
|
||
|
EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
|
||
|
}
|
||
|
|
||
|
TEST(DependencyScanner, ScanDepsReuseFilemanagerSkippedFile) {
|
||
|
std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
|
||
|
StringRef CWD = "/root";
|
||
|
FixedCompilationDatabase CDB(CWD, Compilation);
|
||
|
|
||
|
auto VFS = new llvm::vfs::InMemoryFileSystem();
|
||
|
VFS->setCurrentWorkingDirectory(CWD);
|
||
|
auto Sept = llvm::sys::path::get_separator();
|
||
|
std::string HeaderPath =
|
||
|
std::string(llvm::formatv("{0}root{0}header.h", Sept));
|
||
|
std::string SymlinkPath =
|
||
|
std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
|
||
|
std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
|
||
|
std::string Test2Path =
|
||
|
std::string(llvm::formatv("{0}root{0}test2.cpp", Sept));
|
||
|
|
||
|
VFS->addFile(HeaderPath, 0,
|
||
|
llvm::MemoryBuffer::getMemBuffer("#pragma once\n"));
|
||
|
VFS->addHardLink(SymlinkPath, HeaderPath);
|
||
|
VFS->addFile(TestPath, 0,
|
||
|
llvm::MemoryBuffer::getMemBuffer(
|
||
|
"#include \"header.h\"\n#include \"symlink.h\"\n"));
|
||
|
VFS->addFile(Test2Path, 0,
|
||
|
llvm::MemoryBuffer::getMemBuffer(
|
||
|
"#include \"symlink.h\"\n#include \"header.h\"\n"));
|
||
|
|
||
|
ClangTool Tool(CDB, {"test.cpp", "test2.cpp"},
|
||
|
std::make_shared<PCHContainerOperations>(), VFS);
|
||
|
Tool.clearArgumentsAdjusters();
|
||
|
std::vector<std::string> Deps;
|
||
|
TestDependencyScanningAction Action(Deps);
|
||
|
Tool.run(&Action);
|
||
|
using llvm::sys::path::convert_to_slash;
|
||
|
ASSERT_EQ(Deps.size(), 6u);
|
||
|
EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test2.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[4]), "/root/symlink.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[5]), "/root/header.h");
|
||
|
}
|
||
|
|
||
|
TEST(DependencyScanner, ScanDepsReuseFilemanagerHasInclude) {
|
||
|
std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
|
||
|
StringRef CWD = "/root";
|
||
|
FixedCompilationDatabase CDB(CWD, Compilation);
|
||
|
|
||
|
auto VFS = new llvm::vfs::InMemoryFileSystem();
|
||
|
VFS->setCurrentWorkingDirectory(CWD);
|
||
|
auto Sept = llvm::sys::path::get_separator();
|
||
|
std::string HeaderPath =
|
||
|
std::string(llvm::formatv("{0}root{0}header.h", Sept));
|
||
|
std::string SymlinkPath =
|
||
|
std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
|
||
|
std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
|
||
|
|
||
|
VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
|
||
|
VFS->addHardLink(SymlinkPath, HeaderPath);
|
||
|
VFS->addFile(
|
||
|
TestPath, 0,
|
||
|
llvm::MemoryBuffer::getMemBuffer("#if __has_include(\"header.h\") && "
|
||
|
"__has_include(\"symlink.h\")\n#endif"));
|
||
|
|
||
|
ClangTool Tool(CDB, {"test.cpp", "test.cpp"},
|
||
|
std::make_shared<PCHContainerOperations>(), VFS);
|
||
|
Tool.clearArgumentsAdjusters();
|
||
|
std::vector<std::string> Deps;
|
||
|
TestDependencyScanningAction Action(Deps);
|
||
|
Tool.run(&Action);
|
||
|
using llvm::sys::path::convert_to_slash;
|
||
|
ASSERT_EQ(Deps.size(), 6u);
|
||
|
EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test.cpp");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[4]), "/root/header.h");
|
||
|
EXPECT_EQ(convert_to_slash(Deps[5]), "/root/symlink.h");
|
||
|
}
|
||
|
|
||
|
} // end namespace tooling
|
||
|
} // end namespace clang
|