1 //=== unittests/Sema/CodeCompleteTest.cpp - Code Complete 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/Frontend/CompilerInstance.h"
10 #include "clang/Frontend/FrontendActions.h"
11 #include "clang/Lex/Preprocessor.h"
12 #include "clang/Parse/ParseAST.h"
13 #include "clang/Sema/Sema.h"
14 #include "clang/Sema/SemaDiagnostic.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "llvm/Testing/Support/Annotations.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 #include <cstddef>
20 #include <string>
21
22 namespace {
23
24 using namespace clang;
25 using namespace clang::tooling;
26 using ::testing::Each;
27 using ::testing::UnorderedElementsAre;
28
29 const char TestCCName[] = "test.cc";
30
31 struct CompletionContext {
32 std::vector<std::string> VisitedNamespaces;
33 std::string PreferredType;
34 // String representation of std::ptrdiff_t on a given platform. This is a hack
35 // to properly account for different configurations of clang.
36 std::string PtrDiffType;
37 };
38
39 class VisitedContextFinder : public CodeCompleteConsumer {
40 public:
VisitedContextFinder(CompletionContext & ResultCtx)41 VisitedContextFinder(CompletionContext &ResultCtx)
42 : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}), ResultCtx(ResultCtx),
43 CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {}
44
ProcessCodeCompleteResults(Sema & S,CodeCompletionContext Context,CodeCompletionResult * Results,unsigned NumResults)45 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
46 CodeCompletionResult *Results,
47 unsigned NumResults) override {
48 ResultCtx.VisitedNamespaces =
49 getVisitedNamespace(Context.getVisitedContexts());
50 ResultCtx.PreferredType = Context.getPreferredType().getAsString();
51 ResultCtx.PtrDiffType =
52 S.getASTContext().getPointerDiffType().getAsString();
53 }
54
getAllocator()55 CodeCompletionAllocator &getAllocator() override {
56 return CCTUInfo.getAllocator();
57 }
58
getCodeCompletionTUInfo()59 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
60
61 private:
getVisitedNamespace(CodeCompletionContext::VisitedContextSet VisitedContexts) const62 std::vector<std::string> getVisitedNamespace(
63 CodeCompletionContext::VisitedContextSet VisitedContexts) const {
64 std::vector<std::string> NSNames;
65 for (const auto *Context : VisitedContexts)
66 if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context))
67 NSNames.push_back(NS->getQualifiedNameAsString());
68 return NSNames;
69 }
70
71 CompletionContext &ResultCtx;
72 CodeCompletionTUInfo CCTUInfo;
73 };
74
75 class CodeCompleteAction : public SyntaxOnlyAction {
76 public:
CodeCompleteAction(ParsedSourceLocation P,CompletionContext & ResultCtx)77 CodeCompleteAction(ParsedSourceLocation P, CompletionContext &ResultCtx)
78 : CompletePosition(std::move(P)), ResultCtx(ResultCtx) {}
79
BeginInvocation(CompilerInstance & CI)80 bool BeginInvocation(CompilerInstance &CI) override {
81 CI.getFrontendOpts().CodeCompletionAt = CompletePosition;
82 CI.setCodeCompletionConsumer(new VisitedContextFinder(ResultCtx));
83 return true;
84 }
85
86 private:
87 // 1-based code complete position <Line, Col>;
88 ParsedSourceLocation CompletePosition;
89 CompletionContext &ResultCtx;
90 };
91
offsetToPosition(llvm::StringRef Code,size_t Offset)92 ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) {
93 Offset = std::min(Code.size(), Offset);
94 StringRef Before = Code.substr(0, Offset);
95 int Lines = Before.count('\n');
96 size_t PrevNL = Before.rfind('\n');
97 size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1);
98 return {TestCCName, static_cast<unsigned>(Lines + 1),
99 static_cast<unsigned>(Offset - StartOfLine + 1)};
100 }
101
runCompletion(StringRef Code,size_t Offset)102 CompletionContext runCompletion(StringRef Code, size_t Offset) {
103 CompletionContext ResultCtx;
104 clang::tooling::runToolOnCodeWithArgs(
105 std::make_unique<CodeCompleteAction>(offsetToPosition(Code, Offset),
106 ResultCtx),
107 Code, {"-std=c++11"}, TestCCName);
108 return ResultCtx;
109 }
110
runCodeCompleteOnCode(StringRef AnnotatedCode)111 CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) {
112 llvm::Annotations A(AnnotatedCode);
113 return runCompletion(A.code(), A.point());
114 }
115
116 std::vector<std::string>
collectPreferredTypes(StringRef AnnotatedCode,std::string * PtrDiffType=nullptr)117 collectPreferredTypes(StringRef AnnotatedCode,
118 std::string *PtrDiffType = nullptr) {
119 llvm::Annotations A(AnnotatedCode);
120 std::vector<std::string> Types;
121 for (size_t Point : A.points()) {
122 auto Results = runCompletion(A.code(), Point);
123 if (PtrDiffType) {
124 assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType);
125 *PtrDiffType = Results.PtrDiffType;
126 }
127 Types.push_back(Results.PreferredType);
128 }
129 return Types;
130 }
131
TEST(SemaCodeCompleteTest,VisitedNSForValidQualifiedId)132 TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) {
133 auto VisitedNS = runCodeCompleteOnCode(R"cpp(
134 namespace ns1 {}
135 namespace ns2 {}
136 namespace ns3 {}
137 namespace ns3 { namespace nns3 {} }
138
139 namespace foo {
140 using namespace ns1;
141 namespace ns4 {} // not visited
142 namespace { using namespace ns2; }
143 inline namespace bar { using namespace ns3::nns3; }
144 } // foo
145 namespace ns { foo::^ }
146 )cpp")
147 .VisitedNamespaces;
148 EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3",
149 "foo::(anonymous)"));
150 }
151
TEST(SemaCodeCompleteTest,VisitedNSForInvalidQualifiedId)152 TEST(SemaCodeCompleteTest, VisitedNSForInvalidQualifiedId) {
153 auto VisitedNS = runCodeCompleteOnCode(R"cpp(
154 namespace na {}
155 namespace ns1 {
156 using namespace na;
157 foo::^
158 }
159 )cpp")
160 .VisitedNamespaces;
161 EXPECT_THAT(VisitedNS, UnorderedElementsAre("ns1", "na"));
162 }
163
TEST(SemaCodeCompleteTest,VisitedNSWithoutQualifier)164 TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) {
165 auto VisitedNS = runCodeCompleteOnCode(R"cpp(
166 namespace n1 {
167 namespace n2 {
168 void f(^) {}
169 }
170 }
171 )cpp")
172 .VisitedNamespaces;
173 EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2"));
174 }
175
TEST(PreferredTypeTest,BinaryExpr)176 TEST(PreferredTypeTest, BinaryExpr) {
177 // Check various operations for arithmetic types.
178 StringRef Code = R"cpp(
179 void test(int x) {
180 x = ^10;
181 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;
182 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;
183 })cpp";
184 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
185
186 Code = R"cpp(
187 void test(float x) {
188 x = ^10;
189 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;
190 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;
191 })cpp";
192 EXPECT_THAT(collectPreferredTypes(Code), Each("float"));
193
194 // Pointer types.
195 Code = R"cpp(
196 void test(int *ptr) {
197 ptr - ^ptr;
198 ptr = ^ptr;
199 })cpp";
200 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
201
202 Code = R"cpp(
203 void test(int *ptr) {
204 ptr + ^10;
205 ptr += ^10;
206 ptr -= ^10;
207 })cpp";
208 {
209 std::string PtrDiff;
210 auto Types = collectPreferredTypes(Code, &PtrDiff);
211 EXPECT_THAT(Types, Each(PtrDiff));
212 }
213
214 // Comparison operators.
215 Code = R"cpp(
216 void test(int i) {
217 i <= ^1; i < ^1; i >= ^1; i > ^1; i == ^1; i != ^1;
218 }
219 )cpp";
220 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
221
222 Code = R"cpp(
223 void test(int *ptr) {
224 ptr <= ^ptr; ptr < ^ptr; ptr >= ^ptr; ptr > ^ptr;
225 ptr == ^ptr; ptr != ^ptr;
226 }
227 )cpp";
228 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
229
230 // Relational operations.
231 Code = R"cpp(
232 void test(int i, int *ptr) {
233 i && ^1; i || ^1;
234 ptr && ^1; ptr || ^1;
235 }
236 )cpp";
237 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
238
239 // Bitwise operations.
240 Code = R"cpp(
241 void test(long long ll) {
242 ll | ^1; ll & ^1;
243 }
244 )cpp";
245 EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));
246
247 Code = R"cpp(
248 enum A {};
249 void test(A a) {
250 a | ^1; a & ^1;
251 }
252 )cpp";
253 EXPECT_THAT(collectPreferredTypes(Code), Each("enum A"));
254
255 Code = R"cpp(
256 enum class A {};
257 void test(A a) {
258 // This is technically illegal with the 'enum class' without overloaded
259 // operators, but we pretend it's fine.
260 a | ^a; a & ^a;
261 }
262 )cpp";
263 EXPECT_THAT(collectPreferredTypes(Code), Each("enum A"));
264
265 // Binary shifts.
266 Code = R"cpp(
267 void test(int i, long long ll) {
268 i << ^1; ll << ^1;
269 i <<= ^1; i <<= ^1;
270 i >> ^1; ll >> ^1;
271 i >>= ^1; i >>= ^1;
272 }
273 )cpp";
274 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));
275
276 // Comma does not provide any useful information.
277 Code = R"cpp(
278 class Cls {};
279 void test(int i, int* ptr, Cls x) {
280 (i, ^i);
281 (ptr, ^ptr);
282 (x, ^x);
283 }
284 )cpp";
285 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
286
287 // User-defined types do not take operator overloading into account.
288 // However, they provide heuristics for some common cases.
289 Code = R"cpp(
290 class Cls {};
291 void test(Cls c) {
292 // we assume arithmetic and comparions ops take the same type.
293 c + ^c; c - ^c; c * ^c; c / ^c; c % ^c;
294 c == ^c; c != ^c; c < ^c; c <= ^c; c > ^c; c >= ^c;
295 // same for the assignments.
296 c = ^c; c += ^c; c -= ^c; c *= ^c; c /= ^c; c %= ^c;
297 }
298 )cpp";
299 EXPECT_THAT(collectPreferredTypes(Code), Each("class Cls"));
300
301 Code = R"cpp(
302 class Cls {};
303 void test(Cls c) {
304 // we assume relational ops operate on bools.
305 c && ^c; c || ^c;
306 }
307 )cpp";
308 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
309
310 Code = R"cpp(
311 class Cls {};
312 void test(Cls c) {
313 // we make no assumptions about the following operators, since they are
314 // often overloaded with a non-standard meaning.
315 c << ^c; c >> ^c; c | ^c; c & ^c;
316 c <<= ^c; c >>= ^c; c |= ^c; c &= ^c;
317 }
318 )cpp";
319 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
320 }
321
TEST(PreferredTypeTest,Members)322 TEST(PreferredTypeTest, Members) {
323 StringRef Code = R"cpp(
324 struct vector {
325 int *begin();
326 vector clone();
327 };
328
329 void test(int *a) {
330 a = ^vector().^clone().^begin();
331 }
332 )cpp";
333 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
334 }
335
TEST(PreferredTypeTest,Conditions)336 TEST(PreferredTypeTest, Conditions) {
337 StringRef Code = R"cpp(
338 struct vector {
339 bool empty();
340 };
341
342 void test() {
343 if (^vector().^empty()) {}
344 while (^vector().^empty()) {}
345 for (; ^vector().^empty();) {}
346 }
347 )cpp";
348 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
349 }
350
TEST(PreferredTypeTest,InitAndAssignment)351 TEST(PreferredTypeTest, InitAndAssignment) {
352 StringRef Code = R"cpp(
353 struct vector {
354 int* begin();
355 };
356
357 void test() {
358 const int* x = ^vector().^begin();
359 x = ^vector().^begin();
360
361 if (const int* y = ^vector().^begin()) {}
362 }
363 )cpp";
364 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
365 }
366
TEST(PreferredTypeTest,UnaryExprs)367 TEST(PreferredTypeTest, UnaryExprs) {
368 StringRef Code = R"cpp(
369 void test(long long a) {
370 a = +^a;
371 a = -^a
372 a = ++^a;
373 a = --^a;
374 }
375 )cpp";
376 EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));
377
378 Code = R"cpp(
379 void test(int a, int *ptr) {
380 !^a;
381 !^ptr;
382 !!!^a;
383
384 a = !^a;
385 a = !^ptr;
386 a = !!!^a;
387 }
388 )cpp";
389 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));
390
391 Code = R"cpp(
392 void test(int a) {
393 const int* x = &^a;
394 }
395 )cpp";
396 EXPECT_THAT(collectPreferredTypes(Code), Each("const int"));
397
398 Code = R"cpp(
399 void test(int *a) {
400 int x = *^a;
401 int &r = *^a;
402 }
403 )cpp";
404 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));
405
406 Code = R"cpp(
407 void test(int a) {
408 *^a;
409 &^a;
410 }
411
412 )cpp";
413 }
414
TEST(PreferredTypeTest,ParenExpr)415 TEST(PreferredTypeTest, ParenExpr) {
416 StringRef Code = R"cpp(
417 const int *i = ^(^(^(^10)));
418 )cpp";
419 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
420 }
421
TEST(PreferredTypeTest,FunctionArguments)422 TEST(PreferredTypeTest, FunctionArguments) {
423 StringRef Code = R"cpp(
424 void foo(const int*);
425
426 void bar(const int*);
427 void bar(const int*, int b);
428
429 struct vector {
430 const int *data();
431 };
432 void test() {
433 foo(^(^(^(^vec^tor^().^da^ta^()))));
434 bar(^(^(^(^vec^tor^().^da^ta^()))));
435 }
436 )cpp";
437 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));
438
439 Code = R"cpp(
440 void bar(int, volatile double *);
441 void bar(int, volatile double *, int, int);
442
443 struct vector {
444 double *data();
445 };
446
447 struct class_members {
448 void bar(int, volatile double *);
449 void bar(int, volatile double *, int, int);
450 };
451 void test() {
452 bar(10, ^(^(^(^vec^tor^().^da^ta^()))));
453 class_members().bar(10, ^(^(^(^vec^tor^().^da^ta^()))));
454 }
455 )cpp";
456 EXPECT_THAT(collectPreferredTypes(Code), Each("volatile double *"));
457
458 Code = R"cpp(
459 namespace ns {
460 struct vector {
461 };
462 }
463 void accepts_vector(ns::vector);
464
465 void test() {
466 accepts_vector(^::^ns::^vector());
467 }
468 )cpp";
469 EXPECT_THAT(collectPreferredTypes(Code), Each("ns::vector"));
470
471 Code = R"cpp(
472 template <class T>
473 struct vector { using self = vector; };
474
475 void accepts_vector(vector<int>);
476 int foo(int);
477
478 void test() {
479 accepts_vector(^::^vector<decltype(foo(1))>::^self);
480 }
481 )cpp";
482 EXPECT_THAT(collectPreferredTypes(Code), Each("vector<int>"));
483 }
484
TEST(PreferredTypeTest,NoCrashOnInvalidTypes)485 TEST(PreferredTypeTest, NoCrashOnInvalidTypes) {
486 StringRef Code = R"cpp(
487 auto x = decltype(&1)(^);
488 auto y = new decltype(&1)(^);
489 // GNU decimal type extension is not supported in clang.
490 auto z = new _Decimal128(^);
491 void foo() { (void)(foo)(^); }
492 )cpp";
493 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));
494 }
495
496 } // namespace
497