1 | //===- unittests/Interpreter/CodeCompletionTest.cpp -----------------------===// |
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 "InterpreterTestFixture.h" |
10 | |
11 | #include "clang/Interpreter/CodeCompletion.h" |
12 | #include "clang/Frontend/CompilerInstance.h" |
13 | #include "clang/Interpreter/Interpreter.h" |
14 | #include "clang/Lex/Preprocessor.h" |
15 | #include "clang/Sema/CodeCompleteConsumer.h" |
16 | #include "clang/Sema/Sema.h" |
17 | #include "llvm/LineEditor/LineEditor.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | |
20 | #include "gmock/gmock.h" |
21 | #include "gtest/gtest.h" |
22 | |
23 | using namespace clang; |
24 | namespace { |
25 | auto CB = clang::IncrementalCompilerBuilder(); |
26 | |
27 | class CodeCompletionTest : public InterpreterTestBase { |
28 | public: |
29 | std::unique_ptr<clang::Interpreter> Interp; |
30 | |
31 | void SetUp() override { |
32 | if (!HostSupportsJIT()) |
33 | GTEST_SKIP(); |
34 | std::unique_ptr<CompilerInstance> CI = cantFail(ValOrErr: CB.CreateCpp()); |
35 | this->Interp = cantFail(ValOrErr: clang::Interpreter::create(CI: std::move(CI))); |
36 | } |
37 | |
38 | std::vector<std::string> runComp(llvm::StringRef Input, llvm::Error &ErrR) { |
39 | auto ComplCI = CB.CreateCpp(); |
40 | if (auto Err = ComplCI.takeError()) { |
41 | ErrR = std::move(Err); |
42 | return {}; |
43 | } |
44 | |
45 | auto ComplInterp = clang::Interpreter::create(CI: std::move(*ComplCI)); |
46 | if (auto Err = ComplInterp.takeError()) { |
47 | ErrR = std::move(Err); |
48 | return {}; |
49 | } |
50 | |
51 | std::vector<std::string> Results; |
52 | std::vector<std::string> Comps; |
53 | auto *ParentCI = this->Interp->getCompilerInstance(); |
54 | auto *MainCI = (*ComplInterp)->getCompilerInstance(); |
55 | auto CC = ReplCodeCompleter(); |
56 | CC.codeComplete(InterpCI: MainCI, Content: Input, /* Lines */ Line: 1, Col: Input.size() + 1, ParentCI, |
57 | CCResults&: Results); |
58 | |
59 | for (auto Res : Results) |
60 | if (Res.find(str: CC.Prefix) == 0) |
61 | Comps.push_back(x: Res); |
62 | return Comps; |
63 | } |
64 | }; |
65 | |
66 | TEST_F(CodeCompletionTest, Sanity) { |
67 | cantFail(ValOrErr: Interp->Parse(Code: "int foo = 12;" )); |
68 | auto Err = llvm::Error::success(); |
69 | auto comps = runComp(Input: "f" , ErrR&: Err); |
70 | EXPECT_EQ((size_t)2, comps.size()); // float and foo |
71 | EXPECT_EQ(comps[0], std::string("float" )); |
72 | EXPECT_EQ(comps[1], std::string("foo" )); |
73 | EXPECT_EQ((bool)Err, false); |
74 | } |
75 | |
76 | TEST_F(CodeCompletionTest, SanityNoneValid) { |
77 | cantFail(ValOrErr: Interp->Parse(Code: "int foo = 12;" )); |
78 | auto Err = llvm::Error::success(); |
79 | auto comps = runComp(Input: "babanana" , ErrR&: Err); |
80 | EXPECT_EQ((size_t)0, comps.size()); // foo and float |
81 | EXPECT_EQ((bool)Err, false); |
82 | } |
83 | |
84 | TEST_F(CodeCompletionTest, TwoDecls) { |
85 | cantFail(ValOrErr: Interp->Parse(Code: "int application = 12;" )); |
86 | cantFail(ValOrErr: Interp->Parse(Code: "int apple = 12;" )); |
87 | auto Err = llvm::Error::success(); |
88 | auto comps = runComp(Input: "app" , ErrR&: Err); |
89 | EXPECT_EQ((size_t)2, comps.size()); |
90 | EXPECT_EQ((bool)Err, false); |
91 | } |
92 | |
93 | TEST_F(CodeCompletionTest, CompFunDeclsNoError) { |
94 | auto Err = llvm::Error::success(); |
95 | auto comps = runComp(Input: "void app(" , ErrR&: Err); |
96 | EXPECT_EQ((bool)Err, false); |
97 | } |
98 | |
99 | TEST_F(CodeCompletionTest, TypedDirected) { |
100 | cantFail(ValOrErr: Interp->Parse(Code: "int application = 12;" )); |
101 | cantFail(ValOrErr: Interp->Parse(Code: "char apple = '2';" )); |
102 | cantFail(ValOrErr: Interp->Parse(Code: "void add(int &SomeInt){}" )); |
103 | { |
104 | auto Err = llvm::Error::success(); |
105 | auto comps = runComp(Input: std::string("add(" ), ErrR&: Err); |
106 | EXPECT_EQ((size_t)1, comps.size()); |
107 | EXPECT_EQ((bool)Err, false); |
108 | } |
109 | |
110 | cantFail(ValOrErr: Interp->Parse(Code: "int banana = 42;" )); |
111 | |
112 | { |
113 | auto Err = llvm::Error::success(); |
114 | auto comps = runComp(Input: std::string("add(" ), ErrR&: Err); |
115 | EXPECT_EQ((size_t)2, comps.size()); |
116 | EXPECT_EQ(comps[0], "application" ); |
117 | EXPECT_EQ(comps[1], "banana" ); |
118 | EXPECT_EQ((bool)Err, false); |
119 | } |
120 | |
121 | { |
122 | auto Err = llvm::Error::success(); |
123 | auto comps = runComp(Input: std::string("add(b" ), ErrR&: Err); |
124 | EXPECT_EQ((size_t)1, comps.size()); |
125 | EXPECT_EQ(comps[0], "banana" ); |
126 | EXPECT_EQ((bool)Err, false); |
127 | } |
128 | } |
129 | |
130 | TEST_F(CodeCompletionTest, SanityClasses) { |
131 | cantFail(ValOrErr: Interp->Parse(Code: "struct Apple{};" )); |
132 | cantFail(ValOrErr: Interp->Parse(Code: "void takeApple(Apple &a1){}" )); |
133 | cantFail(ValOrErr: Interp->Parse(Code: "Apple a1;" )); |
134 | cantFail(ValOrErr: Interp->Parse(Code: "void takeAppleCopy(Apple a1){}" )); |
135 | |
136 | { |
137 | auto Err = llvm::Error::success(); |
138 | auto comps = runComp(Input: "takeApple(" , ErrR&: Err); |
139 | EXPECT_EQ((size_t)1, comps.size()); |
140 | EXPECT_EQ(comps[0], std::string("a1" )); |
141 | EXPECT_EQ((bool)Err, false); |
142 | } |
143 | { |
144 | auto Err = llvm::Error::success(); |
145 | auto comps = runComp(Input: std::string("takeAppleCopy(" ), ErrR&: Err); |
146 | EXPECT_EQ((size_t)1, comps.size()); |
147 | EXPECT_EQ(comps[0], std::string("a1" )); |
148 | EXPECT_EQ((bool)Err, false); |
149 | } |
150 | } |
151 | |
152 | TEST_F(CodeCompletionTest, SubClassing) { |
153 | cantFail(ValOrErr: Interp->Parse(Code: "struct Fruit {};" )); |
154 | cantFail(ValOrErr: Interp->Parse(Code: "struct Apple : Fruit{};" )); |
155 | cantFail(ValOrErr: Interp->Parse(Code: "void takeFruit(Fruit &f){}" )); |
156 | cantFail(ValOrErr: Interp->Parse(Code: "Apple a1;" )); |
157 | cantFail(ValOrErr: Interp->Parse(Code: "Fruit f1;" )); |
158 | auto Err = llvm::Error::success(); |
159 | auto comps = runComp(Input: std::string("takeFruit(" ), ErrR&: Err); |
160 | EXPECT_EQ((size_t)2, comps.size()); |
161 | EXPECT_EQ(comps[0], std::string("a1" )); |
162 | EXPECT_EQ(comps[1], std::string("f1" )); |
163 | EXPECT_EQ((bool)Err, false); |
164 | } |
165 | |
166 | TEST_F(CodeCompletionTest, MultipleArguments) { |
167 | cantFail(ValOrErr: Interp->Parse(Code: "int foo = 42;" )); |
168 | cantFail(ValOrErr: Interp->Parse(Code: "char fowl = 'A';" )); |
169 | cantFail(ValOrErr: Interp->Parse(Code: "void takeTwo(int &a, char b){}" )); |
170 | auto Err = llvm::Error::success(); |
171 | auto comps = runComp(Input: std::string("takeTwo(foo, " ), ErrR&: Err); |
172 | EXPECT_EQ((size_t)1, comps.size()); |
173 | EXPECT_EQ(comps[0], std::string("fowl" )); |
174 | EXPECT_EQ((bool)Err, false); |
175 | } |
176 | |
177 | TEST_F(CodeCompletionTest, Methods) { |
178 | cantFail(ValOrErr: Interp->Parse( |
179 | Code: "struct Foo{int add(int a){return 42;} int par(int b){return 42;}};" )); |
180 | cantFail(ValOrErr: Interp->Parse(Code: "Foo f1;" )); |
181 | |
182 | auto Err = llvm::Error::success(); |
183 | auto comps = runComp(Input: std::string("f1." ), ErrR&: Err); |
184 | EXPECT_EQ((size_t)2, comps.size()); |
185 | EXPECT_EQ(comps[0], std::string("add" )); |
186 | EXPECT_EQ(comps[1], std::string("par" )); |
187 | EXPECT_EQ((bool)Err, false); |
188 | } |
189 | |
190 | TEST_F(CodeCompletionTest, MethodsInvocations) { |
191 | cantFail(ValOrErr: Interp->Parse( |
192 | Code: "struct Foo{int add(int a){return 42;} int par(int b){return 42;}};" )); |
193 | cantFail(ValOrErr: Interp->Parse(Code: "Foo f1;" )); |
194 | cantFail(ValOrErr: Interp->Parse(Code: "int a = 84;" )); |
195 | |
196 | auto Err = llvm::Error::success(); |
197 | auto comps = runComp(Input: std::string("f1.add(" ), ErrR&: Err); |
198 | EXPECT_EQ((size_t)1, comps.size()); |
199 | EXPECT_EQ(comps[0], std::string("a" )); |
200 | EXPECT_EQ((bool)Err, false); |
201 | } |
202 | |
203 | TEST_F(CodeCompletionTest, NestedInvocations) { |
204 | cantFail(ValOrErr: Interp->Parse( |
205 | Code: "struct Foo{int add(int a){return 42;} int par(int b){return 42;}};" )); |
206 | cantFail(ValOrErr: Interp->Parse(Code: "Foo f1;" )); |
207 | cantFail(ValOrErr: Interp->Parse(Code: "int a = 84;" )); |
208 | cantFail(ValOrErr: Interp->Parse(Code: "int plus(int a, int b) { return a + b; }" )); |
209 | |
210 | auto Err = llvm::Error::success(); |
211 | auto comps = runComp(Input: std::string("plus(42, f1.add(" ), ErrR&: Err); |
212 | EXPECT_EQ((size_t)1, comps.size()); |
213 | EXPECT_EQ(comps[0], std::string("a" )); |
214 | EXPECT_EQ((bool)Err, false); |
215 | } |
216 | |
217 | TEST_F(CodeCompletionTest, TemplateFunctions) { |
218 | cantFail( |
219 | ValOrErr: Interp->Parse(Code: "template <typename T> T id(T a) { return a;} " )); |
220 | cantFail(ValOrErr: Interp->Parse(Code: "int apple = 84;" )); |
221 | { |
222 | auto Err = llvm::Error::success(); |
223 | auto comps = runComp(Input: std::string("id<int>(" ), ErrR&: Err); |
224 | EXPECT_EQ((size_t)1, comps.size()); |
225 | EXPECT_EQ(comps[0], std::string("apple" )); |
226 | EXPECT_EQ((bool)Err, false); |
227 | } |
228 | |
229 | cantFail(ValOrErr: Interp->Parse( |
230 | Code: "template <typename T> T pickFirst(T a, T b) { return a;} " )); |
231 | cantFail(ValOrErr: Interp->Parse(Code: "char pear = '4';" )); |
232 | { |
233 | auto Err = llvm::Error::success(); |
234 | auto comps = runComp(Input: std::string("pickFirst(apple, " ), ErrR&: Err); |
235 | EXPECT_EQ((size_t)1, comps.size()); |
236 | EXPECT_EQ(comps[0], std::string("apple" )); |
237 | EXPECT_EQ((bool)Err, false); |
238 | } |
239 | } |
240 | |
241 | } // anonymous namespace |
242 | |