| 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 | |