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