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