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