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:
35   TestFileCollector(DependencyOutputOptions &Opts,
36                     std::vector<std::string> &Deps)
37       : DependencyFileGenerator(Opts), Deps(Deps) {}
38 
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:
50   TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}
51 
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 
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 =
87       std::string(llvm::formatv("{0}root{0}header.h", Sept));
88   std::string SymlinkPath =
89       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
90   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
91 
92   VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
93   VFS->addHardLink(SymlinkPath, HeaderPath);
94   VFS->addFile(TestPath, 0,
95                llvm::MemoryBuffer::getMemBuffer(
96                    "#include \"symlink.h\"\n#include \"header.h\"\n"));
97 
98   ClangTool Tool(CDB, {"test.cpp"}, std::make_shared<PCHContainerOperations>(),
99                  VFS);
100   Tool.clearArgumentsAdjusters();
101   std::vector<std::string> Deps;
102   TestDependencyScanningAction Action(Deps);
103   Tool.run(&Action);
104   using llvm::sys::path::convert_to_slash;
105   // The first invocation should return dependencies in order of access.
106   ASSERT_EQ(Deps.size(), 3u);
107   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
108   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
109   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
110 
111   // The file manager should still have two FileEntries, as one file is a
112   // hardlink.
113   FileManager &Files = Tool.getFiles();
114   EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
115 
116   Deps.clear();
117   Tool.run(&Action);
118   // The second invocation should have the same order of dependencies.
119   ASSERT_EQ(Deps.size(), 3u);
120   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
121   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");
122   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");
123 
124   EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);
125 }
126 
127 TEST(DependencyScanner, ScanDepsReuseFilemanagerSkippedFile) {
128   std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
129   StringRef CWD = "/root";
130   FixedCompilationDatabase CDB(CWD, Compilation);
131 
132   auto VFS = new llvm::vfs::InMemoryFileSystem();
133   VFS->setCurrentWorkingDirectory(CWD);
134   auto Sept = llvm::sys::path::get_separator();
135   std::string HeaderPath =
136       std::string(llvm::formatv("{0}root{0}header.h", Sept));
137   std::string SymlinkPath =
138       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
139   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
140   std::string Test2Path =
141       std::string(llvm::formatv("{0}root{0}test2.cpp", Sept));
142 
143   VFS->addFile(HeaderPath, 0,
144                llvm::MemoryBuffer::getMemBuffer("#pragma once\n"));
145   VFS->addHardLink(SymlinkPath, HeaderPath);
146   VFS->addFile(TestPath, 0,
147                llvm::MemoryBuffer::getMemBuffer(
148                    "#include \"header.h\"\n#include \"symlink.h\"\n"));
149   VFS->addFile(Test2Path, 0,
150                llvm::MemoryBuffer::getMemBuffer(
151                    "#include \"symlink.h\"\n#include \"header.h\"\n"));
152 
153   ClangTool Tool(CDB, {"test.cpp", "test2.cpp"},
154                  std::make_shared<PCHContainerOperations>(), VFS);
155   Tool.clearArgumentsAdjusters();
156   std::vector<std::string> Deps;
157   TestDependencyScanningAction Action(Deps);
158   Tool.run(&Action);
159   using llvm::sys::path::convert_to_slash;
160   ASSERT_EQ(Deps.size(), 6u);
161   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
162   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
163   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
164   EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test2.cpp");
165   EXPECT_EQ(convert_to_slash(Deps[4]), "/root/symlink.h");
166   EXPECT_EQ(convert_to_slash(Deps[5]), "/root/header.h");
167 }
168 
169 TEST(DependencyScanner, ScanDepsReuseFilemanagerHasInclude) {
170   std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};
171   StringRef CWD = "/root";
172   FixedCompilationDatabase CDB(CWD, Compilation);
173 
174   auto VFS = new llvm::vfs::InMemoryFileSystem();
175   VFS->setCurrentWorkingDirectory(CWD);
176   auto Sept = llvm::sys::path::get_separator();
177   std::string HeaderPath =
178       std::string(llvm::formatv("{0}root{0}header.h", Sept));
179   std::string SymlinkPath =
180       std::string(llvm::formatv("{0}root{0}symlink.h", Sept));
181   std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
182 
183   VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
184   VFS->addHardLink(SymlinkPath, HeaderPath);
185   VFS->addFile(
186       TestPath, 0,
187       llvm::MemoryBuffer::getMemBuffer("#if __has_include(\"header.h\") && "
188                                        "__has_include(\"symlink.h\")\n#endif"));
189 
190   ClangTool Tool(CDB, {"test.cpp", "test.cpp"},
191                  std::make_shared<PCHContainerOperations>(), VFS);
192   Tool.clearArgumentsAdjusters();
193   std::vector<std::string> Deps;
194   TestDependencyScanningAction Action(Deps);
195   Tool.run(&Action);
196   using llvm::sys::path::convert_to_slash;
197   ASSERT_EQ(Deps.size(), 6u);
198   EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");
199   EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");
200   EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");
201   EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test.cpp");
202   EXPECT_EQ(convert_to_slash(Deps[4]), "/root/header.h");
203   EXPECT_EQ(convert_to_slash(Deps[5]), "/root/symlink.h");
204 }
205 
206 } // end namespace tooling
207 } // end namespace clang
208