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