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 = 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 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 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