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