1 //===- unittest/Tooling/ExecutionTest.cpp - Tool execution 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/Tooling/Execution.h" 10 #include "clang/AST/ASTConsumer.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/AST/DynamicRecursiveASTVisitor.h" 13 #include "clang/Frontend/ASTUnit.h" 14 #include "clang/Frontend/FrontendAction.h" 15 #include "clang/Frontend/FrontendActions.h" 16 #include "clang/Tooling/AllTUsExecution.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "clang/Tooling/StandaloneExecution.h" 19 #include "clang/Tooling/ToolExecutorPluginRegistry.h" 20 #include "clang/Tooling/Tooling.h" 21 #include "gmock/gmock.h" 22 #include "gtest/gtest.h" 23 #include <algorithm> 24 #include <string> 25 26 namespace clang { 27 namespace tooling { 28 29 namespace { 30 31 // This traverses the AST and outputs function name as key and "1" as value for 32 // each function declaration. 33 class ASTConsumerWithResult : public ASTConsumer, 34 public DynamicRecursiveASTVisitor { 35 public: 36 explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) { 37 assert(Context != nullptr); 38 } 39 40 void HandleTranslationUnit(clang::ASTContext &Context) override { 41 TraverseDecl(Context.getTranslationUnitDecl()); 42 } 43 44 bool TraverseFunctionDecl(clang::FunctionDecl *Decl) override { 45 Context->reportResult(Decl->getNameAsString(), 46 Context->getRevision() + ":" + Context->getCorpus() + 47 ":" + Context->getCurrentCompilationUnit() + 48 "/1"); 49 return DynamicRecursiveASTVisitor::TraverseFunctionDecl(Decl); 50 } 51 52 private: 53 ExecutionContext *const Context; 54 }; 55 56 class ReportResultAction : public ASTFrontendAction { 57 public: 58 explicit ReportResultAction(ExecutionContext *Context) : Context(Context) { 59 assert(Context != nullptr); 60 } 61 62 protected: 63 std::unique_ptr<clang::ASTConsumer> 64 CreateASTConsumer(clang::CompilerInstance &compiler, 65 StringRef /* dummy */) override { 66 std::unique_ptr<clang::ASTConsumer> ast_consumer{ 67 new ASTConsumerWithResult(Context)}; 68 return ast_consumer; 69 } 70 71 private: 72 ExecutionContext *const Context; 73 }; 74 75 class ReportResultActionFactory : public FrontendActionFactory { 76 public: 77 ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {} 78 std::unique_ptr<FrontendAction> create() override { 79 return std::make_unique<ReportResultAction>(Context); 80 } 81 82 private: 83 ExecutionContext *const Context; 84 }; 85 86 } // namespace 87 88 class TestToolExecutor : public ToolExecutor { 89 public: 90 static const char *ExecutorName; 91 92 TestToolExecutor(CommonOptionsParser Options) 93 : OptionsParser(std::move(Options)) {} 94 95 StringRef getExecutorName() const override { return ExecutorName; } 96 97 llvm::Error 98 execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>, 99 ArgumentsAdjuster>>) override { 100 return llvm::Error::success(); 101 } 102 103 ExecutionContext *getExecutionContext() override { return nullptr; }; 104 105 ToolResults *getToolResults() override { return nullptr; } 106 107 llvm::ArrayRef<std::string> getSourcePaths() const { 108 return OptionsParser.getSourcePathList(); 109 } 110 111 void mapVirtualFile(StringRef FilePath, StringRef Content) override { 112 VFS[std::string(FilePath)] = std::string(Content); 113 } 114 115 private: 116 CommonOptionsParser OptionsParser; 117 std::string SourcePaths; 118 std::map<std::string, std::string> VFS; 119 }; 120 121 const char *TestToolExecutor::ExecutorName = "test-executor"; 122 123 class TestToolExecutorPlugin : public ToolExecutorPlugin { 124 public: 125 llvm::Expected<std::unique_ptr<ToolExecutor>> 126 create(CommonOptionsParser &OptionsParser) override { 127 return std::make_unique<TestToolExecutor>(std::move(OptionsParser)); 128 } 129 }; 130 131 static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin> 132 X("test-executor", "Plugin for TestToolExecutor."); 133 134 llvm::cl::OptionCategory TestCategory("execution-test options"); 135 136 TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) { 137 std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"}; 138 int argc = argv.size(); 139 auto Executor = internal::createExecutorFromCommandLineArgsImpl( 140 argc, &argv[0], TestCategory); 141 ASSERT_FALSE((bool)Executor); 142 llvm::consumeError(Executor.takeError()); 143 } 144 145 TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) { 146 llvm::cl::opt<std::string> BeforeReset( 147 "before_reset", llvm::cl::desc("Defined before reset."), 148 llvm::cl::init("")); 149 150 llvm::cl::ResetAllOptionOccurrences(); 151 152 std::vector<const char *> argv = {"prog", "--before_reset=set", "f"}; 153 int argc = argv.size(); 154 auto Executor = internal::createExecutorFromCommandLineArgsImpl( 155 argc, &argv[0], TestCategory); 156 ASSERT_TRUE((bool)Executor); 157 EXPECT_EQ(BeforeReset, "set"); 158 BeforeReset.removeArgument(); 159 } 160 161 TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) { 162 std::vector<const char *> argv = {"prog", "standalone.cpp"}; 163 int argc = argv.size(); 164 auto Executor = internal::createExecutorFromCommandLineArgsImpl( 165 argc, &argv[0], TestCategory); 166 ASSERT_TRUE((bool)Executor); 167 EXPECT_EQ(Executor->get()->getExecutorName(), 168 StandaloneToolExecutor::ExecutorName); 169 } 170 171 TEST(CreateToolExecutorTest, CreateTestToolExecutor) { 172 std::vector<const char *> argv = {"prog", "test.cpp", 173 "--executor=test-executor"}; 174 int argc = argv.size(); 175 auto Executor = internal::createExecutorFromCommandLineArgsImpl( 176 argc, &argv[0], TestCategory); 177 ASSERT_TRUE((bool)Executor); 178 EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName); 179 } 180 181 TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) { 182 FixedCompilationDatabase Compilations(".", std::vector<std::string>()); 183 StandaloneToolExecutor Executor(Compilations, 184 std::vector<std::string>(1, "a.cc")); 185 Executor.mapVirtualFile("a.cc", "int x = 0;"); 186 187 auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(), 188 getClangSyntaxOnlyAdjuster()); 189 ASSERT_TRUE(!Err); 190 } 191 192 TEST(StandaloneToolTest, SimpleAction) { 193 FixedCompilationDatabase Compilations(".", std::vector<std::string>()); 194 StandaloneToolExecutor Executor(Compilations, 195 std::vector<std::string>(1, "a.cc")); 196 Executor.mapVirtualFile("a.cc", "int x = 0;"); 197 198 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( 199 new ReportResultActionFactory(Executor.getExecutionContext()))); 200 ASSERT_TRUE(!Err); 201 auto KVs = Executor.getToolResults()->AllKVResults(); 202 ASSERT_EQ(KVs.size(), 0u); 203 } 204 205 TEST(StandaloneToolTest, SimpleActionWithResult) { 206 FixedCompilationDatabase Compilations(".", std::vector<std::string>()); 207 StandaloneToolExecutor Executor(Compilations, 208 std::vector<std::string>(1, "a.cc")); 209 Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}"); 210 211 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( 212 new ReportResultActionFactory(Executor.getExecutionContext()))); 213 ASSERT_TRUE(!Err); 214 auto KVs = Executor.getToolResults()->AllKVResults(); 215 ASSERT_EQ(KVs.size(), 1u); 216 EXPECT_EQ("f", KVs[0].first); 217 // Currently the standlone executor returns empty corpus, revision, and 218 // compilation unit. 219 EXPECT_EQ("::/1", KVs[0].second); 220 221 Executor.getToolResults()->forEachResult( 222 [](StringRef, StringRef Value) { EXPECT_EQ("::/1", Value); }); 223 } 224 225 class FixedCompilationDatabaseWithFiles : public CompilationDatabase { 226 public: 227 FixedCompilationDatabaseWithFiles(Twine Directory, 228 ArrayRef<std::string> Files, 229 ArrayRef<std::string> CommandLine) 230 : FixedCompilations(Directory, CommandLine), Files(Files) {} 231 232 std::vector<CompileCommand> 233 getCompileCommands(StringRef FilePath) const override { 234 return FixedCompilations.getCompileCommands(FilePath); 235 } 236 237 std::vector<std::string> getAllFiles() const override { return Files; } 238 239 private: 240 FixedCompilationDatabase FixedCompilations; 241 std::vector<std::string> Files; 242 }; 243 244 MATCHER_P(Named, Name, "") { return arg.first == Name; } 245 246 TEST(AllTUsToolTest, AFewFiles) { 247 FixedCompilationDatabaseWithFiles Compilations( 248 ".", {"a.cc", "b.cc", "c.cc", "ignore.cc"}, std::vector<std::string>()); 249 AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0); 250 Filter.setValue("[a-c].cc"); 251 Executor.mapVirtualFile("a.cc", "void x() {}"); 252 Executor.mapVirtualFile("b.cc", "void y() {}"); 253 Executor.mapVirtualFile("c.cc", "void z() {}"); 254 Executor.mapVirtualFile("ignore.cc", "void d() {}"); 255 256 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( 257 new ReportResultActionFactory(Executor.getExecutionContext()))); 258 ASSERT_TRUE(!Err); 259 EXPECT_THAT( 260 Executor.getToolResults()->AllKVResults(), 261 ::testing::UnorderedElementsAre(Named("x"), Named("y"), Named("z"))); 262 Filter.setValue(".*"); // reset to default value. 263 } 264 265 TEST(AllTUsToolTest, ManyFiles) { 266 unsigned NumFiles = 100; 267 std::vector<std::string> Files; 268 std::map<std::string, std::string> FileToContent; 269 std::vector<std::string> ExpectedSymbols; 270 for (unsigned i = 1; i <= NumFiles; ++i) { 271 std::string File = "f" + std::to_string(i) + ".cc"; 272 std::string Symbol = "looong_function_name_" + std::to_string(i); 273 Files.push_back(File); 274 FileToContent[File] = "void " + Symbol + "() {}"; 275 ExpectedSymbols.push_back(Symbol); 276 } 277 FixedCompilationDatabaseWithFiles Compilations(".", Files, 278 std::vector<std::string>()); 279 AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0); 280 for (const auto &FileAndContent : FileToContent) { 281 Executor.mapVirtualFile(FileAndContent.first, FileAndContent.second); 282 } 283 284 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( 285 new ReportResultActionFactory(Executor.getExecutionContext()))); 286 ASSERT_TRUE(!Err); 287 std::vector<std::string> Results; 288 Executor.getToolResults()->forEachResult( 289 [&](StringRef Name, StringRef) { Results.push_back(std::string(Name)); }); 290 EXPECT_THAT(ExpectedSymbols, ::testing::UnorderedElementsAreArray(Results)); 291 } 292 293 } // end namespace tooling 294 } // end namespace clang 295