1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "clang/AST/ASTConsumer.h"
10 #include "clang/AST/DeclCXX.h"
11 #include "clang/AST/DeclGroup.h"
12 #include "clang/Frontend/ASTUnit.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Frontend/FrontendAction.h"
15 #include "clang/Frontend/FrontendActions.h"
16 #include "clang/Tooling/CompilationDatabase.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/FormatVariadic.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/TargetRegistry.h"
22 #include "llvm/Support/TargetSelect.h"
23 #include "gtest/gtest.h"
24 #include <algorithm>
25 #include <string>
26
27 namespace clang {
28 namespace tooling {
29
30 namespace {
31
32 /// Prints out all of the gathered dependencies into a string.
33 class TestFileCollector : public DependencyFileGenerator {
34 public:
TestFileCollector(DependencyOutputOptions & Opts,std::vector<std::string> & Deps)35 TestFileCollector(DependencyOutputOptions &Opts,
36 std::vector<std::string> &Deps)
37 : DependencyFileGenerator(Opts), Deps(Deps) {}
38
finishedMainFile(DiagnosticsEngine & Diags)39 void finishedMainFile(DiagnosticsEngine &Diags) override {
40 auto NewDeps = getDependencies();
41 Deps.insert(Deps.end(), NewDeps.begin(), NewDeps.end());
42 }
43
44 private:
45 std::vector<std::string> &Deps;
46 };
47
48 class TestDependencyScanningAction : public tooling::ToolAction {
49 public:
TestDependencyScanningAction(std::vector<std::string> & Deps)50 TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}
51
runInvocation(std::shared_ptr<CompilerInvocation> Invocation,FileManager * FileMgr,std::shared_ptr<PCHContainerOperations> PCHContainerOps,DiagnosticConsumer * DiagConsumer)52 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
53 FileManager *FileMgr,
54 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
55 DiagnosticConsumer *DiagConsumer) override {
56 CompilerInstance Compiler(std::move(PCHContainerOps));
57 Compiler.setInvocation(std::move(Invocation));
58 Compiler.setFileManager(FileMgr);
59
60 Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
61 if (!Compiler.hasDiagnostics())
62 return false;
63
64 Compiler.createSourceManager(*FileMgr);
65 Compiler.addDependencyCollector(std::make_shared<TestFileCollector>(
66 Compiler.getInvocation().getDependencyOutputOpts(), Deps));
67
68 auto Action = std::make_unique<PreprocessOnlyAction>();
69 return Compiler.ExecuteAction(*Action);
70 }
71
72 private:
73 std::vector<std::string> &Deps;
74 };
75
76 } // namespace
77
TEST(DependencyScanner,ScanDepsReuseFilemanager)78 TEST(DependencyScanner, ScanDepsReuseFilemanager) {
79 std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
80 StringRef CWD = "/root";
81 FixedCompilationDatabase CDB(CWD, Compilation);
82
83 auto VFS = new llvm::vfs::InMemoryFileSystem();
84 VFS->setCurrentWorkingDirectory(CWD);
85 auto Sept = llvm::sys::path::get_separator();
86 std::string HeaderPath = llvm::formatv("{0}root{0}header.h", Sept);
87 std::string SymlinkPath = llvm::formatv("{0}root{0}symlink.h", Sept);
88 std::string TestPath = llvm::formatv("{0}root{0}test.cpp", Sept);
89
90 VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
91 VFS->addHardLink(SymlinkPath, HeaderPath);
92 VFS->addFile(TestPath, 0,
93 llvm::MemoryBuffer::getMemBuffer(
94 "#include \"symlink.h\"\n#include \"header.h\"\n"));
95
96 ClangTool Tool(CDB, {"test.cpp"}, std::make_shared<PCHContainerOperations>(),
97 VFS);
98 Tool.clearArgumentsAdjusters();
99 std::vector<std::string> Deps;
100 TestDependencyScanningAction Action(Deps);
101 Tool.run(&Action);
102 using llvm::sys::path::convert_to_slash;
103 // The first invocation should return dependencies in order of access.
104 ASSERT_EQ(Deps.size(), 3u);
105 EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
106 EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
107 EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
108
109 // The file manager should still have two FileEntries, as one file is a
110 // hardlink.
111 FileManager &Files = Tool.getFiles();
112 EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
113
114 Deps.clear();
115 Tool.run(&Action);
116 // The second invocation should have the same order of dependencies.
117 ASSERT_EQ(Deps.size(), 3u);
118 EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
119 EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
120 EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
121
122 EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
123 }
124
TEST(DependencyScanner,ScanDepsReuseFilemanagerSkippedFile)125 TEST(DependencyScanner, ScanDepsReuseFilemanagerSkippedFile) {
126 std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
127 StringRef CWD = "/root";
128 FixedCompilationDatabase CDB(CWD, Compilation);
129
130 auto VFS = new llvm::vfs::InMemoryFileSystem();
131 VFS->setCurrentWorkingDirectory(CWD);
132 auto Sept = llvm::sys::path::get_separator();
133 std::string HeaderPath = llvm::formatv("{0}root{0}header.h", Sept);
134 std::string SymlinkPath = llvm::formatv("{0}root{0}symlink.h", Sept);
135 std::string TestPath = llvm::formatv("{0}root{0}test.cpp", Sept);
136 std::string Test2Path = llvm::formatv("{0}root{0}test2.cpp", Sept);
137
138 VFS->addFile(HeaderPath, 0,
139 llvm::MemoryBuffer::getMemBuffer("#pragma once\n"));
140 VFS->addHardLink(SymlinkPath, HeaderPath);
141 VFS->addFile(TestPath, 0,
142 llvm::MemoryBuffer::getMemBuffer(
143 "#include \"header.h\"\n#include \"symlink.h\"\n"));
144 VFS->addFile(Test2Path, 0,
145 llvm::MemoryBuffer::getMemBuffer(
146 "#include \"symlink.h\"\n#include \"header.h\"\n"));
147
148 ClangTool Tool(CDB, {"test.cpp", "test2.cpp"},
149 std::make_shared<PCHContainerOperations>(), VFS);
150 Tool.clearArgumentsAdjusters();
151 std::vector<std::string> Deps;
152 TestDependencyScanningAction Action(Deps);
153 Tool.run(&Action);
154 using llvm::sys::path::convert_to_slash;
155 ASSERT_EQ(Deps.size(), 6u);
156 EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
157 EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
158 EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
159 EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test2.cpp");
160 EXPECT_EQ(convert_to_slash(Deps[4]), "/root/symlink.h");
161 EXPECT_EQ(convert_to_slash(Deps[5]), "/root/header.h");
162 }
163
TEST(DependencyScanner,ScanDepsReuseFilemanagerHasInclude)164 TEST(DependencyScanner, ScanDepsReuseFilemanagerHasInclude) {
165 std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
166 StringRef CWD = "/root";
167 FixedCompilationDatabase CDB(CWD, Compilation);
168
169 auto VFS = new llvm::vfs::InMemoryFileSystem();
170 VFS->setCurrentWorkingDirectory(CWD);
171 auto Sept = llvm::sys::path::get_separator();
172 std::string HeaderPath = llvm::formatv("{0}root{0}header.h", Sept);
173 std::string SymlinkPath = llvm::formatv("{0}root{0}symlink.h", Sept);
174 std::string TestPath = llvm::formatv("{0}root{0}test.cpp", Sept);
175
176 VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
177 VFS->addHardLink(SymlinkPath, HeaderPath);
178 VFS->addFile(
179 TestPath, 0,
180 llvm::MemoryBuffer::getMemBuffer("#if __has_include(\"header.h\") && "
181 "__has_include(\"symlink.h\")\n#endif"));
182
183 ClangTool Tool(CDB, {"test.cpp", "test.cpp"},
184 std::make_shared<PCHContainerOperations>(), VFS);
185 Tool.clearArgumentsAdjusters();
186 std::vector<std::string> Deps;
187 TestDependencyScanningAction Action(Deps);
188 Tool.run(&Action);
189 using llvm::sys::path::convert_to_slash;
190 ASSERT_EQ(Deps.size(), 6u);
191 EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
192 EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
193 EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
194 EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test.cpp");
195 EXPECT_EQ(convert_to_slash(Deps[4]), "/root/header.h");
196 EXPECT_EQ(convert_to_slash(Deps[5]), "/root/symlink.h");
197 }
198
199 } // end namespace tooling
200 } // end namespace clang
201