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/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/CompilationDatabase.h"
17 #include "clang/Tooling/Execution.h"
18 #include "clang/Tooling/StandaloneExecution.h"
19 #include "clang/Tooling/ToolExecutorPluginRegistry.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gtest/gtest.h"
22 #include <algorithm>
23 #include <string>
24 
25 namespace clang {
26 namespace tooling {
27 
28 namespace {
29 
30 // This traverses the AST and outputs function name as key and "1" as value for
31 // each function declaration.
32 class ASTConsumerWithResult
33     : public ASTConsumer,
34       public RecursiveASTVisitor<ASTConsumerWithResult> {
35 public:
36   using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>;
37 
38   explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) {
39     assert(Context != nullptr);
40   }
41 
42   void HandleTranslationUnit(clang::ASTContext &Context) override {
43     TraverseDecl(Context.getTranslationUnitDecl());
44   }
45 
46   bool TraverseFunctionDecl(clang::FunctionDecl *Decl) {
47     Context->reportResult(Decl->getNameAsString(), "1");
48     return ASTVisitor::TraverseFunctionDecl(Decl);
49   }
50 
51 private:
52   ExecutionContext *const Context;
53 };
54 
55 class ReportResultAction : public ASTFrontendAction {
56 public:
57   explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {
58     assert(Context != nullptr);
59   }
60 
61 protected:
62   std::unique_ptr<clang::ASTConsumer>
63   CreateASTConsumer(clang::CompilerInstance &compiler,
64                     StringRef /* dummy */) override {
65     std::unique_ptr<clang::ASTConsumer> ast_consumer{
66         new ASTConsumerWithResult(Context)};
67     return ast_consumer;
68   }
69 
70 private:
71   ExecutionContext *const Context;
72 };
73 
74 class ReportResultActionFactory : public FrontendActionFactory {
75 public:
76   ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}
77   FrontendAction *create() override { return new ReportResultAction(Context); }
78 
79 private:
80   ExecutionContext *const Context;
81 };
82 
83 } // namespace
84 
85 class TestToolExecutor : public ToolExecutor {
86 public:
87   static const char *ExecutorName;
88 
89   TestToolExecutor(CommonOptionsParser Options)
90       : OptionsParser(std::move(Options)) {}
91 
92   StringRef getExecutorName() const override { return ExecutorName; }
93 
94   llvm::Error
95   execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,
96                                    ArgumentsAdjuster>>) override {
97     return llvm::Error::success();
98   }
99 
100   ExecutionContext *getExecutionContext() override { return nullptr; };
101 
102   ToolResults *getToolResults() override { return nullptr; }
103 
104   llvm::ArrayRef<std::string> getSourcePaths() const {
105     return OptionsParser.getSourcePathList();
106   }
107 
108   void mapVirtualFile(StringRef FilePath, StringRef Content) override {
109     VFS[FilePath] = Content;
110   }
111 
112 private:
113   CommonOptionsParser OptionsParser;
114   std::string SourcePaths;
115   std::map<std::string, std::string> VFS;
116 };
117 
118 const char *TestToolExecutor::ExecutorName = "test-executor";
119 
120 class TestToolExecutorPlugin : public ToolExecutorPlugin {
121 public:
122   llvm::Expected<std::unique_ptr<ToolExecutor>>
123   create(CommonOptionsParser &OptionsParser) override {
124     return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser));
125   }
126 };
127 
128 static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
129     X("test-executor", "Plugin for TestToolExecutor.");
130 
131 llvm::cl::OptionCategory TestCategory("execution-test options");
132 
133 TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {
134   std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};
135   int argc = argv.size();
136   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
137       argc, &argv[0], TestCategory);
138   ASSERT_FALSE((bool)Executor);
139   llvm::consumeError(Executor.takeError());
140 }
141 
142 TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {
143   llvm::cl::opt<std::string> BeforeReset(
144       "before_reset", llvm::cl::desc("Defined before reset."),
145       llvm::cl::init(""));
146 
147   llvm::cl::ResetAllOptionOccurrences();
148 
149   std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};
150   int argc = argv.size();
151   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
152       argc, &argv[0], TestCategory);
153   ASSERT_TRUE((bool)Executor);
154   EXPECT_EQ(BeforeReset, "set");
155   BeforeReset.removeArgument();
156 }
157 
158 TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {
159   std::vector<const char *> argv = {"prog", "standalone.cpp"};
160   int argc = argv.size();
161   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
162       argc, &argv[0], TestCategory);
163   ASSERT_TRUE((bool)Executor);
164   EXPECT_EQ(Executor->get()->getExecutorName(),
165             StandaloneToolExecutor::ExecutorName);
166 }
167 
168 TEST(CreateToolExecutorTest, CreateTestToolExecutor) {
169   std::vector<const char *> argv = {"prog", "test.cpp",
170                                     "--executor=test-executor"};
171   int argc = argv.size();
172   auto Executor = internal::createExecutorFromCommandLineArgsImpl(
173       argc, &argv[0], TestCategory);
174   ASSERT_TRUE((bool)Executor);
175   EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
176 }
177 
178 TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {
179   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
180   StandaloneToolExecutor Executor(Compilations,
181                                   std::vector<std::string>(1, "a.cc"));
182   Executor.mapVirtualFile("a.cc", "int x = 0;");
183 
184   auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(),
185                               getClangSyntaxOnlyAdjuster());
186   ASSERT_TRUE(!Err);
187 }
188 
189 TEST(StandaloneToolTest, SimpleAction) {
190   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
191   StandaloneToolExecutor Executor(Compilations,
192                                   std::vector<std::string>(1, "a.cc"));
193   Executor.mapVirtualFile("a.cc", "int x = 0;");
194 
195   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
196       new ReportResultActionFactory(Executor.getExecutionContext())));
197   ASSERT_TRUE(!Err);
198   auto KVs = Executor.getToolResults()->AllKVResults();
199   ASSERT_EQ(KVs.size(), 0u);
200 }
201 
202 TEST(StandaloneToolTest, SimpleActionWithResult) {
203   FixedCompilationDatabase Compilations(".", std::vector<std::string>());
204   StandaloneToolExecutor Executor(Compilations,
205                                   std::vector<std::string>(1, "a.cc"));
206   Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}");
207 
208   auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(
209       new ReportResultActionFactory(Executor.getExecutionContext())));
210   ASSERT_TRUE(!Err);
211   auto KVs = Executor.getToolResults()->AllKVResults();
212   ASSERT_EQ(KVs.size(), 1u);
213   EXPECT_EQ("f", KVs[0].first);
214   EXPECT_EQ("1", KVs[0].second);
215 
216   Executor.getToolResults()->forEachResult(
217       [](StringRef, StringRef Value) { EXPECT_EQ("1", Value); });
218 }
219 
220 } // end namespace tooling
221 } // end namespace clang
222