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