1 | //===-- ASTTests.cpp --------------------------------------------*- C++ -*-===// |
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 "AST.h" |
10 | |
11 | #include "Annotations.h" |
12 | #include "ParsedAST.h" |
13 | #include "TestTU.h" |
14 | #include "index/Symbol.h" |
15 | #include "clang/AST/ASTTypeTraits.h" |
16 | #include "clang/AST/Attr.h" |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/DeclBase.h" |
19 | #include "clang/Basic/AttrKinds.h" |
20 | #include "clang/Basic/SourceManager.h" |
21 | #include "llvm/ADT/StringRef.h" |
22 | #include "llvm/Support/Casting.h" |
23 | #include "gmock/gmock.h" |
24 | #include "gtest/gtest.h" |
25 | #include <cstddef> |
26 | #include <string> |
27 | #include <vector> |
28 | |
29 | namespace clang { |
30 | namespace clangd { |
31 | namespace { |
32 | using testing::Contains; |
33 | using testing::Each; |
34 | using testing::IsEmpty; |
35 | |
36 | TEST(GetDeducedType, KwAutoKwDecltypeExpansion) { |
37 | struct Test { |
38 | StringRef AnnotatedCode; |
39 | const char *DeducedType; |
40 | } Tests[] = { |
41 | {.AnnotatedCode: "^auto i = 0;" , .DeducedType: "int" }, |
42 | {.AnnotatedCode: "^auto f(){ return 1;};" , .DeducedType: "int" }, |
43 | { |
44 | .AnnotatedCode: R"cpp( // auto on struct in a namespace |
45 | namespace ns1 { struct S {}; } |
46 | ^auto v = ns1::S{}; |
47 | )cpp" , |
48 | .DeducedType: "ns1::S" , |
49 | }, |
50 | { |
51 | .AnnotatedCode: R"cpp( // decltype on struct |
52 | namespace ns1 { struct S {}; } |
53 | ns1::S i; |
54 | ^decltype(i) j; |
55 | )cpp" , |
56 | .DeducedType: "ns1::S" , |
57 | }, |
58 | { |
59 | .AnnotatedCode: R"cpp(// decltype(auto) on struct& |
60 | namespace ns1 { |
61 | struct S {}; |
62 | } // namespace ns1 |
63 | |
64 | ns1::S i; |
65 | ns1::S& j = i; |
66 | ^decltype(auto) k = j; |
67 | )cpp" , |
68 | .DeducedType: "ns1::S &" , |
69 | }, |
70 | { |
71 | .AnnotatedCode: R"cpp( // auto on template class |
72 | class X; |
73 | template<typename T> class Foo {}; |
74 | ^auto v = Foo<X>(); |
75 | )cpp" , |
76 | .DeducedType: "Foo<X>" , |
77 | }, |
78 | { |
79 | .AnnotatedCode: R"cpp( // auto on initializer list. |
80 | namespace std |
81 | { |
82 | template<class _E> |
83 | class [[initializer_list]] {}; |
84 | } |
85 | |
86 | ^auto i = {1,2}; |
87 | )cpp" , |
88 | .DeducedType: "std::initializer_list<int>" , |
89 | }, |
90 | { |
91 | .AnnotatedCode: R"cpp( // auto in function return type with trailing return type |
92 | struct Foo {}; |
93 | ^auto test() -> decltype(Foo()) { |
94 | return Foo(); |
95 | } |
96 | )cpp" , |
97 | .DeducedType: "Foo" , |
98 | }, |
99 | { |
100 | .AnnotatedCode: R"cpp( // decltype in trailing return type |
101 | struct Foo {}; |
102 | auto test() -> ^decltype(Foo()) { |
103 | return Foo(); |
104 | } |
105 | )cpp" , |
106 | .DeducedType: "Foo" , |
107 | }, |
108 | { |
109 | .AnnotatedCode: R"cpp( // auto in function return type |
110 | struct Foo {}; |
111 | ^auto test() { |
112 | return Foo(); |
113 | } |
114 | )cpp" , |
115 | .DeducedType: "Foo" , |
116 | }, |
117 | { |
118 | .AnnotatedCode: R"cpp( // auto& in function return type |
119 | struct Foo {}; |
120 | ^auto& test() { |
121 | static Foo x; |
122 | return x; |
123 | } |
124 | )cpp" , |
125 | .DeducedType: "Foo" , |
126 | }, |
127 | { |
128 | .AnnotatedCode: R"cpp( // auto* in function return type |
129 | struct Foo {}; |
130 | ^auto* test() { |
131 | Foo *x; |
132 | return x; |
133 | } |
134 | )cpp" , |
135 | .DeducedType: "Foo" , |
136 | }, |
137 | { |
138 | .AnnotatedCode: R"cpp( // const auto& in function return type |
139 | struct Foo {}; |
140 | const ^auto& test() { |
141 | static Foo x; |
142 | return x; |
143 | } |
144 | )cpp" , |
145 | .DeducedType: "Foo" , |
146 | }, |
147 | { |
148 | .AnnotatedCode: R"cpp( // decltype(auto) in function return (value) |
149 | struct Foo {}; |
150 | ^decltype(auto) test() { |
151 | return Foo(); |
152 | } |
153 | )cpp" , |
154 | .DeducedType: "Foo" , |
155 | }, |
156 | { |
157 | .AnnotatedCode: R"cpp( // decltype(auto) in function return (ref) |
158 | struct Foo {}; |
159 | ^decltype(auto) test() { |
160 | static Foo x; |
161 | return (x); |
162 | } |
163 | )cpp" , |
164 | .DeducedType: "Foo &" , |
165 | }, |
166 | { |
167 | .AnnotatedCode: R"cpp( // decltype(auto) in function return (const ref) |
168 | struct Foo {}; |
169 | ^decltype(auto) test() { |
170 | static const Foo x; |
171 | return (x); |
172 | } |
173 | )cpp" , |
174 | .DeducedType: "const Foo &" , |
175 | }, |
176 | { |
177 | .AnnotatedCode: R"cpp( // auto on alias |
178 | struct Foo {}; |
179 | using Bar = Foo; |
180 | ^auto x = Bar(); |
181 | )cpp" , |
182 | .DeducedType: "Bar" , |
183 | }, |
184 | { |
185 | .AnnotatedCode: R"cpp( |
186 | // Generic lambda param. |
187 | struct Foo{}; |
188 | auto Generic = [](^auto x) { return 0; }; |
189 | int m = Generic(Foo{}); |
190 | )cpp" , |
191 | .DeducedType: "struct Foo" , |
192 | }, |
193 | { |
194 | .AnnotatedCode: R"cpp( |
195 | // Generic lambda instantiated twice, matching deduction. |
196 | struct Foo{}; |
197 | auto Generic = [](^auto x, auto y) { return 0; }; |
198 | int m = Generic(Foo{}, "one"); |
199 | int n = Generic(Foo{}, 2); |
200 | )cpp" , |
201 | // No deduction although both instantiations yield the same result :-( |
202 | .DeducedType: nullptr, |
203 | }, |
204 | { |
205 | .AnnotatedCode: R"cpp( |
206 | // Generic lambda instantiated twice, conflicting deduction. |
207 | struct Foo{}; |
208 | auto Generic = [](^auto y) { return 0; }; |
209 | int m = Generic("one"); |
210 | int n = Generic(2); |
211 | )cpp" , |
212 | .DeducedType: nullptr, |
213 | }, |
214 | { |
215 | .AnnotatedCode: R"cpp( |
216 | // Generic function param. |
217 | struct Foo{}; |
218 | int generic(^auto x) { return 0; } |
219 | int m = generic(Foo{}); |
220 | )cpp" , |
221 | .DeducedType: "struct Foo" , |
222 | }, |
223 | { |
224 | .AnnotatedCode: R"cpp( |
225 | // More complicated param type involving auto. |
226 | template <class> concept C = true; |
227 | struct Foo{}; |
228 | int generic(C ^auto *x) { return 0; } |
229 | const Foo *Ptr = nullptr; |
230 | int m = generic(Ptr); |
231 | )cpp" , |
232 | .DeducedType: "const struct Foo" , |
233 | }, |
234 | }; |
235 | for (Test T : Tests) { |
236 | Annotations File(T.AnnotatedCode); |
237 | auto TU = TestTU::withCode(Code: File.code()); |
238 | TU.ExtraArgs.push_back(x: "-std=c++20" ); |
239 | auto AST = TU.build(); |
240 | SourceManagerForFile SM("foo.cpp" , File.code()); |
241 | |
242 | SCOPED_TRACE(T.AnnotatedCode); |
243 | EXPECT_FALSE(File.points().empty()); |
244 | for (Position Pos : File.points()) { |
245 | auto Location = sourceLocationInMainFile(SM: SM.get(), P: Pos); |
246 | ASSERT_TRUE(!!Location) << llvm::toString(E: Location.takeError()); |
247 | auto DeducedType = getDeducedType(AST.getASTContext(), *Location); |
248 | if (T.DeducedType == nullptr) { |
249 | EXPECT_FALSE(DeducedType); |
250 | } else { |
251 | ASSERT_TRUE(DeducedType); |
252 | EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); |
253 | } |
254 | } |
255 | } |
256 | } |
257 | |
258 | TEST(ClangdAST, GetOnlyInstantiation) { |
259 | struct { |
260 | const char *Code; |
261 | llvm::StringLiteral NodeType; |
262 | const char *Name; |
263 | } Cases[] = { |
264 | { |
265 | .Code: R"cpp( |
266 | template <typename> class X {}; |
267 | X<int> x; |
268 | )cpp" , |
269 | .NodeType: "CXXRecord" , |
270 | .Name: "template<> class X<int> {}" , |
271 | }, |
272 | { |
273 | .Code: R"cpp( |
274 | template <typename T> T X = T{}; |
275 | int y = X<char>; |
276 | )cpp" , |
277 | .NodeType: "Var" , |
278 | // VarTemplateSpecializationDecl doesn't print as template<>... |
279 | .Name: "char X = char{}" , |
280 | }, |
281 | { |
282 | .Code: R"cpp( |
283 | template <typename T> int X(T) { return 42; } |
284 | int y = X("text"); |
285 | )cpp" , |
286 | .NodeType: "Function" , |
287 | .Name: "template<> int X<const char *>(const char *)" , |
288 | }, |
289 | { |
290 | .Code: R"cpp( |
291 | int X(auto *x) { return 42; } |
292 | int y = X("text"); |
293 | )cpp" , |
294 | .NodeType: "Function" , |
295 | .Name: "template<> int X<const char>(const char *x)" , |
296 | }, |
297 | }; |
298 | |
299 | for (const auto &Case : Cases) { |
300 | SCOPED_TRACE(Case.Code); |
301 | auto TU = TestTU::withCode(Code: Case.Code); |
302 | TU.ExtraArgs.push_back(x: "-std=c++20" ); |
303 | auto AST = TU.build(); |
304 | PrintingPolicy PP = AST.getASTContext().getPrintingPolicy(); |
305 | PP.TerseOutput = true; |
306 | std::string Name; |
307 | if (auto *Result = getOnlyInstantiation( |
308 | const_cast<NamedDecl *>(&findDecl(AST, [&](const NamedDecl &D) { |
309 | return D.getDescribedTemplate() != nullptr && |
310 | D.getDeclKindName() == Case.NodeType; |
311 | })))) { |
312 | llvm::raw_string_ostream OS(Name); |
313 | Result->print(OS, PP); |
314 | } |
315 | |
316 | if (Case.Name) |
317 | EXPECT_EQ(Case.Name, Name); |
318 | else |
319 | EXPECT_THAT(Name, IsEmpty()); |
320 | } |
321 | } |
322 | |
323 | TEST(ClangdAST, GetContainedAutoParamType) { |
324 | auto TU = TestTU::withCode(Code: R"cpp( |
325 | int withAuto( |
326 | auto a, |
327 | auto *b, |
328 | const auto *c, |
329 | auto &&d, |
330 | auto *&e, |
331 | auto (*f)(int) |
332 | ){}; |
333 | |
334 | int withoutAuto( |
335 | int a, |
336 | int *b, |
337 | const int *c, |
338 | int &&d, |
339 | int *&e, |
340 | int (*f)(int) |
341 | ){}; |
342 | )cpp" ); |
343 | TU.ExtraArgs.push_back(x: "-std=c++20" ); |
344 | auto AST = TU.build(); |
345 | |
346 | const auto &WithAuto = |
347 | llvm::cast<FunctionTemplateDecl>(Val: findDecl(AST, QName: "withAuto" )); |
348 | auto ParamsWithAuto = WithAuto.getTemplatedDecl()->parameters(); |
349 | auto *TemplateParamsWithAuto = WithAuto.getTemplateParameters(); |
350 | ASSERT_EQ(ParamsWithAuto.size(), TemplateParamsWithAuto->size()); |
351 | |
352 | for (unsigned I = 0; I < ParamsWithAuto.size(); ++I) { |
353 | SCOPED_TRACE(ParamsWithAuto[I]->getNameAsString()); |
354 | auto Loc = getContainedAutoParamType( |
355 | ParamsWithAuto[I]->getTypeSourceInfo()->getTypeLoc()); |
356 | ASSERT_FALSE(Loc.isNull()); |
357 | EXPECT_EQ(Loc.getTypePtr()->getDecl(), TemplateParamsWithAuto->getParam(I)); |
358 | } |
359 | |
360 | const auto &WithoutAuto = |
361 | llvm::cast<FunctionDecl>(Val: findDecl(AST, QName: "withoutAuto" )); |
362 | for (auto *ParamWithoutAuto : WithoutAuto.parameters()) { |
363 | ASSERT_TRUE(getContainedAutoParamType( |
364 | ParamWithoutAuto->getTypeSourceInfo()->getTypeLoc()) |
365 | .isNull()); |
366 | } |
367 | } |
368 | |
369 | TEST(ClangdAST, GetQualification) { |
370 | // Tries to insert the decl `Foo` into position of each decl named `insert`. |
371 | // This is done to get an appropriate DeclContext for the insertion location. |
372 | // Qualifications are the required nested name specifier to spell `Foo` at the |
373 | // `insert`ion location. |
374 | // VisibleNamespaces are assumed to be visible at every insertion location. |
375 | const struct { |
376 | llvm::StringRef Test; |
377 | std::vector<llvm::StringRef> Qualifications; |
378 | std::vector<std::string> VisibleNamespaces; |
379 | } Cases[] = { |
380 | { |
381 | .Test: R"cpp( |
382 | namespace ns1 { namespace ns2 { class Foo {}; } } |
383 | void insert(); // ns1::ns2::Foo |
384 | namespace ns1 { |
385 | void insert(); // ns2::Foo |
386 | namespace ns2 { |
387 | void insert(); // Foo |
388 | } |
389 | using namespace ns2; |
390 | void insert(); // Foo |
391 | } |
392 | using namespace ns1; |
393 | void insert(); // ns2::Foo |
394 | using namespace ns2; |
395 | void insert(); // Foo |
396 | )cpp" , |
397 | .Qualifications: {"ns1::ns2::" , "ns2::" , "" , "" , "ns2::" , "" }, |
398 | .VisibleNamespaces: {}, |
399 | }, |
400 | { |
401 | .Test: R"cpp( |
402 | namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } } |
403 | void insert(); // ns1::ns2::Bar::Foo |
404 | namespace ns1 { |
405 | void insert(); // ns2::Bar::Foo |
406 | namespace ns2 { |
407 | void insert(); // Bar::Foo |
408 | } |
409 | using namespace ns2; |
410 | void insert(); // Bar::Foo |
411 | } |
412 | using namespace ns1; |
413 | void insert(); // ns2::Bar::Foo |
414 | using namespace ns2; |
415 | void insert(); // Bar::Foo |
416 | )cpp" , |
417 | .Qualifications: {"ns1::ns2::Bar::" , "ns2::Bar::" , "Bar::" , "Bar::" , "ns2::Bar::" , |
418 | "Bar::" }, |
419 | .VisibleNamespaces: {}, |
420 | }, |
421 | { |
422 | .Test: R"cpp( |
423 | namespace ns1 { namespace ns2 { void Foo(); } } |
424 | void insert(); // ns2::Foo |
425 | namespace ns1 { |
426 | void insert(); // ns2::Foo |
427 | namespace ns2 { |
428 | void insert(); // Foo |
429 | } |
430 | } |
431 | )cpp" , |
432 | .Qualifications: {"ns2::" , "ns2::" , "" }, |
433 | .VisibleNamespaces: {"ns1::" }, |
434 | }, |
435 | { |
436 | .Test: R"cpp( |
437 | namespace ns { |
438 | extern "C" { |
439 | typedef int Foo; |
440 | } |
441 | } |
442 | void insert(); // ns::Foo |
443 | )cpp" , |
444 | .Qualifications: {"ns::" }, |
445 | .VisibleNamespaces: {}, |
446 | }, |
447 | }; |
448 | for (const auto &Case : Cases) { |
449 | Annotations Test(Case.Test); |
450 | TestTU TU = TestTU::withCode(Code: Test.code()); |
451 | ParsedAST AST = TU.build(); |
452 | std::vector<const Decl *> InsertionPoints; |
453 | const NamedDecl *TargetDecl; |
454 | findDecl(AST, Filter: [&](const NamedDecl &ND) { |
455 | if (ND.getNameAsString() == "Foo" ) { |
456 | TargetDecl = &ND; |
457 | return true; |
458 | } |
459 | |
460 | if (ND.getNameAsString() == "insert" ) |
461 | InsertionPoints.push_back(&ND); |
462 | return false; |
463 | }); |
464 | |
465 | ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size()); |
466 | for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { |
467 | const Decl *D = InsertionPoints[I]; |
468 | if (Case.VisibleNamespaces.empty()) { |
469 | EXPECT_EQ(getQualification(AST.getASTContext(), |
470 | D->getLexicalDeclContext(), D->getBeginLoc(), |
471 | TargetDecl), |
472 | Case.Qualifications[I]); |
473 | } else { |
474 | EXPECT_EQ(getQualification(AST.getASTContext(), |
475 | D->getLexicalDeclContext(), TargetDecl, |
476 | Case.VisibleNamespaces), |
477 | Case.Qualifications[I]); |
478 | } |
479 | } |
480 | } |
481 | } |
482 | |
483 | TEST(ClangdAST, PrintType) { |
484 | const struct { |
485 | llvm::StringRef Test; |
486 | std::vector<llvm::StringRef> Types; |
487 | } Cases[] = { |
488 | { |
489 | .Test: R"cpp( |
490 | namespace ns1 { namespace ns2 { class Foo {}; } } |
491 | void insert(); // ns1::ns2::Foo |
492 | namespace ns1 { |
493 | void insert(); // ns2::Foo |
494 | namespace ns2 { |
495 | void insert(); // Foo |
496 | } |
497 | } |
498 | )cpp" , |
499 | .Types: {"ns1::ns2::Foo" , "ns2::Foo" , "Foo" }, |
500 | }, |
501 | { |
502 | .Test: R"cpp( |
503 | namespace ns1 { |
504 | typedef int Foo; |
505 | } |
506 | void insert(); // ns1::Foo |
507 | namespace ns1 { |
508 | void insert(); // Foo |
509 | } |
510 | )cpp" , |
511 | .Types: {"ns1::Foo" , "Foo" }, |
512 | }, |
513 | }; |
514 | for (const auto &Case : Cases) { |
515 | Annotations Test(Case.Test); |
516 | TestTU TU = TestTU::withCode(Code: Test.code()); |
517 | ParsedAST AST = TU.build(); |
518 | std::vector<const DeclContext *> InsertionPoints; |
519 | const TypeDecl *TargetDecl = nullptr; |
520 | findDecl(AST, Filter: [&](const NamedDecl &ND) { |
521 | if (ND.getNameAsString() == "Foo" ) { |
522 | if (const auto *TD = llvm::dyn_cast<TypeDecl>(Val: &ND)) { |
523 | TargetDecl = TD; |
524 | return true; |
525 | } |
526 | } else if (ND.getNameAsString() == "insert" ) |
527 | InsertionPoints.push_back(ND.getDeclContext()); |
528 | return false; |
529 | }); |
530 | |
531 | ASSERT_EQ(InsertionPoints.size(), Case.Types.size()); |
532 | for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { |
533 | const auto *DC = InsertionPoints[I]; |
534 | EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC), |
535 | Case.Types[I]); |
536 | } |
537 | } |
538 | } |
539 | |
540 | TEST(ClangdAST, IsDeeplyNested) { |
541 | Annotations Test( |
542 | R"cpp( |
543 | namespace ns { |
544 | class Foo { |
545 | void bar() { |
546 | class Bar {}; |
547 | } |
548 | }; |
549 | })cpp" ); |
550 | TestTU TU = TestTU::withCode(Code: Test.code()); |
551 | ParsedAST AST = TU.build(); |
552 | |
553 | EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Foo" ), /*MaxDepth=*/1)); |
554 | EXPECT_FALSE( |
555 | isDeeplyNested(&findUnqualifiedDecl(AST, "Foo" ), /*MaxDepth=*/2)); |
556 | |
557 | EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "bar" ), /*MaxDepth=*/2)); |
558 | EXPECT_FALSE( |
559 | isDeeplyNested(&findUnqualifiedDecl(AST, "bar" ), /*MaxDepth=*/3)); |
560 | |
561 | EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Bar" ), /*MaxDepth=*/3)); |
562 | EXPECT_FALSE( |
563 | isDeeplyNested(&findUnqualifiedDecl(AST, "Bar" ), /*MaxDepth=*/4)); |
564 | } |
565 | |
566 | MATCHER_P(attrKind, K, "" ) { return arg->getKind() == K; } |
567 | |
568 | MATCHER(implicitAttr, "" ) { return arg->isImplicit(); } |
569 | |
570 | TEST(ClangdAST, GetAttributes) { |
571 | const char *Code = R"cpp( |
572 | class X{}; |
573 | class [[nodiscard]] Y{}; |
574 | void f(int * a, int * __attribute__((nonnull)) b); |
575 | void foo(bool c) { |
576 | if (c) |
577 | [[unlikely]] return; |
578 | } |
579 | )cpp" ; |
580 | ParsedAST AST = TestTU::withCode(Code).build(); |
581 | auto DeclAttrs = [&](llvm::StringRef Name) { |
582 | return getAttributes(DynTypedNode::create(Node: findUnqualifiedDecl(AST, Name))); |
583 | }; |
584 | // Implicit attributes may be present (e.g. visibility on windows). |
585 | ASSERT_THAT(DeclAttrs("X" ), Each(implicitAttr())); |
586 | ASSERT_THAT(DeclAttrs("Y" ), Contains(attrKind(attr::WarnUnusedResult))); |
587 | ASSERT_THAT(DeclAttrs("f" ), Each(implicitAttr())); |
588 | ASSERT_THAT(DeclAttrs("a" ), Each(implicitAttr())); |
589 | ASSERT_THAT(DeclAttrs("b" ), Contains(attrKind(attr::NonNull))); |
590 | |
591 | Stmt *FooBody = cast<FunctionDecl>(Val: findDecl(AST, QName: "foo" )).getBody(); |
592 | IfStmt *FooIf = cast<IfStmt>(Val: cast<CompoundStmt>(Val: FooBody)->body_front()); |
593 | ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf)), |
594 | Each(implicitAttr())); |
595 | ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf->getThen())), |
596 | Contains(attrKind(attr::Unlikely))); |
597 | } |
598 | |
599 | TEST(ClangdAST, HasReservedName) { |
600 | ParsedAST AST = TestTU::withCode(Code: R"cpp( |
601 | void __foo(); |
602 | namespace std { |
603 | inline namespace __1 { class error_code; } |
604 | namespace __detail { int secret; } |
605 | } |
606 | )cpp" ) |
607 | .build(); |
608 | |
609 | EXPECT_TRUE(hasReservedName(findUnqualifiedDecl(AST, "__foo" ))); |
610 | EXPECT_FALSE( |
611 | hasReservedScope(*findUnqualifiedDecl(AST, "__foo" ).getDeclContext())); |
612 | |
613 | EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "error_code" ))); |
614 | EXPECT_FALSE(hasReservedScope( |
615 | *findUnqualifiedDecl(AST, "error_code" ).getDeclContext())); |
616 | |
617 | EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "secret" ))); |
618 | EXPECT_TRUE( |
619 | hasReservedScope(*findUnqualifiedDecl(AST, "secret" ).getDeclContext())); |
620 | } |
621 | |
622 | TEST(ClangdAST, PreferredIncludeDirective) { |
623 | auto ComputePreferredDirective = [](TestTU &TU) { |
624 | auto AST = TU.build(); |
625 | return preferredIncludeDirective(FileName: AST.tuPath(), LangOpts: AST.getLangOpts(), |
626 | MainFileIncludes: AST.getIncludeStructure().MainFileIncludes, |
627 | TopLevelDecls: AST.getLocalTopLevelDecls()); |
628 | }; |
629 | TestTU ObjCTU = TestTU::withCode(Code: R"cpp( |
630 | int main() {} |
631 | )cpp" ); |
632 | ObjCTU.Filename = "TestTU.m" ; |
633 | EXPECT_EQ(ComputePreferredDirective(ObjCTU), |
634 | Symbol::IncludeDirective::Import); |
635 | |
636 | TestTU = TestTU::withCode(Code: R"cpp( |
637 | #import "TestTU.h" |
638 | )cpp" ); |
639 | HeaderTU.Filename = "TestTUHeader.h" ; |
640 | HeaderTU.ExtraArgs = {"-xobjective-c++-header" }; |
641 | EXPECT_EQ(ComputePreferredDirective(HeaderTU), |
642 | Symbol::IncludeDirective::Import); |
643 | |
644 | // ObjC language option is not enough for headers. |
645 | HeaderTU.Code = R"cpp( |
646 | #include "TestTU.h" |
647 | )cpp" ; |
648 | EXPECT_EQ(ComputePreferredDirective(HeaderTU), |
649 | Symbol::IncludeDirective::Include); |
650 | |
651 | HeaderTU.Code = R"cpp( |
652 | @interface Foo |
653 | @end |
654 | |
655 | Foo * getFoo(); |
656 | )cpp" ; |
657 | EXPECT_EQ(ComputePreferredDirective(HeaderTU), |
658 | Symbol::IncludeDirective::Import); |
659 | } |
660 | |
661 | } // namespace |
662 | } // namespace clangd |
663 | } // namespace clang |
664 | |