1 module contract.templates;
2 
3 
4 import contract;
5 
6 
7 @Tags("contract")
8 @("Partial and full template specialisation")
9 @safe unittest {
10     const tu = parse(
11         Cpp(
12             q{
13                 struct Foo; struct Bar; struct Baz; struct Quux;
14 
15                 template<typename, typename, bool, typename, int, typename>
16                 struct Template { using Type = bool; };
17 
18                 template<typename T, bool V0, typename T3, typename T4>
19                 struct Template<Quux, T, V0, T3, 42, T4> { using Type = short; };
20 
21                 template<typename T, bool V0, typename T3, typename T4>
22                 struct Template<T, Quux, V0, T3, 42, T4> { using Type = double; };
23 
24                 template<>
25                 struct Template<Quux, Baz, true, Bar, 33, Foo> { using Type = float; };
26             }
27         )
28     );
29 
30     tu.children.length.shouldEqual(8);
31 
32     auto structs = tu.children[0 .. 4];     // Foo, Bar, Baz, Quux
33     auto template_ = tu.children[4];        // The full or pure template
34     auto partials = tu.children[5 .. 7];    // The partial template specialisations
35     auto full = tu.children[7]; // The last Template declaration
36 
37     foreach(struct_; structs) {
38         struct_.kind.should == Cursor.Kind.StructDecl;
39         struct_.type.numTemplateArguments.should == -1;
40     }
41 
42     template_.kind.should == Cursor.Kind.ClassTemplate;
43     // The actual template, according to clang, has no template arguments
44     template_.type.numTemplateArguments.should == -1;
45     // To get the template parameters, one must look at the ClassTemplate's children
46     template_.children.length.should == 7;
47     printChildren(template_);
48 
49     const typeAliasDecl = template_.children[$ - 1];
50     typeAliasDecl.kind.should == Cursor.Kind.TypeAliasDecl;
51 
52     const templateParameters = template_.children[0 .. $ - 1];
53     templateParameters[0].kind.should == Cursor.Kind.TemplateTypeParameter;
54     templateParameters[1].kind.should == Cursor.Kind.TemplateTypeParameter;
55     templateParameters[2].kind.should == Cursor.Kind.NonTypeTemplateParameter;
56     templateParameters[3].kind.should == Cursor.Kind.TemplateTypeParameter; // bool
57     templateParameters[4].kind.should == Cursor.Kind.NonTypeTemplateParameter;
58     templateParameters[5].kind.should == Cursor.Kind.TemplateTypeParameter; // int
59 
60     foreach(partial; partials) {
61         partial.kind.should == Cursor.Kind.ClassTemplatePartialSpecialization;
62         partial.type.numTemplateArguments.should == 6;
63         partial.specializedCursorTemplate.should == template_;
64     }
65 
66     full.kind.should == Cursor.Kind.StructDecl;
67     full.type.numTemplateArguments.should == 6;
68 }
69 
70 
71 
72 @Tags("contract")
73 @("variadic.only.types")
74 @safe unittest {
75     import clang: Token;
76 
77     const tu = parse(
78         Cpp(
79             q{
80                 template<typename...>
81                 struct Variadic {};
82             }
83         )
84     );
85 
86     tu.children.length.shouldEqual(1);
87 
88     const variadic = tu.children[0];
89     printChildren(variadic);
90 
91     variadic.kind.should == Cursor.Kind.ClassTemplate;
92     variadic.type.numTemplateArguments.should == -1;
93 
94     // variadic templates can't use the children to figure out how many template
95     // arguments there are, since there's only one "typename" and the length
96     // can be any number.
97     variadic.children.length.should == 1;
98     const templateParameter = variadic.children[0];
99 
100     templateParameter.kind.should == Cursor.Kind.TemplateTypeParameter;
101     templateParameter.type.kind.should == Type.Kind.Unexposed;
102     templateParameter.type.canonical.kind.should == Type.Kind.Unexposed;
103     templateParameter.type.spelling.should == "type-parameter-0-0";
104 
105     Token(Token.Kind.Punctuation, "...").should.be in variadic.tokens;
106 }
107 
108 @Tags("contract")
109 @("variadic.only.values")
110 @safe unittest {
111     import clang: Token;
112 
113     const tu = parse(
114         Cpp(
115             q{
116                 template<int...>
117                 struct Variadic {};
118             }
119         )
120     );
121 
122     tu.children.length.shouldEqual(1);
123 
124     const variadic = tu.children[0];
125     printChildren(variadic);
126 
127     variadic.kind.should == Cursor.Kind.ClassTemplate;
128     variadic.type.numTemplateArguments.should == -1;
129 
130     // variadic templates can't use the children to figure out how many template
131     // arguments there are, since there's only one "typename" and the length
132     // can be any number.
133     variadic.children.length.should == 1;
134     const templateParameter = variadic.children[0];
135 
136     templateParameter.kind.should == Cursor.Kind.NonTypeTemplateParameter;
137     templateParameter.type.kind.should == Type.Kind.Int;
138     templateParameter.type.canonical.kind.should == Type.Kind.Int;
139     templateParameter.type.spelling.should == "int";
140 
141     Token(Token.Kind.Punctuation, "...").should.be in variadic.tokens;
142 }
143 
144 @Tags("contract")
145 @("variadic.also.types")
146 @safe unittest {
147     import clang: Token;
148 
149     const tu = parse(
150         Cpp(
151             q{
152                 template<int, typename, bool, typename...>
153                 struct Variadic {};
154             }
155         )
156     );
157 
158     tu.children.length.shouldEqual(1);
159 
160     const variadic = tu.children[0];
161     printChildren(variadic);
162 
163     variadic.kind.should == Cursor.Kind.ClassTemplate;
164     variadic.type.numTemplateArguments.should == -1;
165 
166     // variadic templates can't use the children to figure out how many template
167     // arguments there are, since there's only one "typename" and the length
168     // can be any number.
169     variadic.children.length.should == 4;
170     const intParam = variadic.children[0];
171     const typeParam = variadic.children[1];
172     const boolParam = variadic.children[2];
173     const restParam = variadic.children[3];
174 
175     intParam.kind.should == Cursor.Kind.NonTypeTemplateParameter;
176     intParam.type.kind.should == Type.Kind.Int;
177 
178     typeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
179     typeParam.type.kind.should == Type.Kind.Unexposed;
180     typeParam.spelling.should == "";
181 
182     boolParam.kind.should == Cursor.Kind.NonTypeTemplateParameter;
183     boolParam.type.kind.should == Type.Kind.Bool;
184 
185     restParam.kind.should == Cursor.Kind.TemplateTypeParameter;
186     restParam.type.kind.should == Type.Kind.Unexposed;
187     restParam.type.spelling.should == "type-parameter-0-3";
188 
189     Token(Token.Kind.Punctuation, "...").should.be in variadic.tokens;
190 }
191 
192 @Tags("contract")
193 @("variadic.also.values")
194 @safe unittest {
195     import clang: Token;
196 
197     const tu = parse(
198         Cpp(
199             q{
200                 template<int, typename, bool, short...>
201                 struct Variadic {};
202             }
203         )
204     );
205 
206     tu.children.length.shouldEqual(1);
207 
208     const variadic = tu.children[0];
209     printChildren(variadic);
210 
211     variadic.kind.should == Cursor.Kind.ClassTemplate;
212     variadic.type.numTemplateArguments.should == -1;
213 
214     // variadic templates can't use the children to figure out how many template
215     // arguments there are, since there's only one "typename" and the length
216     // can be any number.
217     variadic.children.length.should == 4;
218     const intParam = variadic.children[0];
219     const typeParam = variadic.children[1];
220     const boolParam = variadic.children[2];
221     const restParam = variadic.children[3];
222 
223     intParam.kind.should == Cursor.Kind.NonTypeTemplateParameter;
224     intParam.type.kind.should == Type.Kind.Int;
225 
226     typeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
227     typeParam.type.kind.should == Type.Kind.Unexposed;
228     typeParam.spelling.should == "";
229 
230     boolParam.kind.should == Cursor.Kind.NonTypeTemplateParameter;
231     boolParam.type.kind.should == Type.Kind.Bool;
232 
233     restParam.kind.should == Cursor.Kind.NonTypeTemplateParameter;
234     restParam.type.kind.should == Type.Kind.Short;
235     restParam.type.spelling.should == "short";
236 
237     Token(Token.Kind.Punctuation, "...").should.be in variadic.tokens;
238 }
239 
240 
241 
242 @Tags("contract")
243 @("variadic.specialization")
244 @safe unittest {
245     import clang: Token;
246 
247     const tu = parse(
248         Cpp(
249             q{
250                 template<typename...>
251                 struct Variadic {};
252 
253                 template<typename T0, typename T1>
254                 struct Variadic<T0, T1> { };
255             }
256         )
257     );
258 
259     tu.children.length.shouldEqual(2);
260 
261     const template_ = tu.children[0];
262     printChildren(template_);
263 
264     const special = tu.children[1];
265     printChildren(special);
266 
267     special.kind.should == Cursor.Kind.ClassTemplatePartialSpecialization;
268     special.type.numTemplateArguments.should == 2;
269     // unexposed - non-specialised type
270     special.type.typeTemplateArgument(0).kind.should == Type.Kind.Unexposed;
271     special.type.typeTemplateArgument(1).kind.should == Type.Kind.Unexposed;
272 }
273 
274 
275 @Tags("contract")
276 @("lref")
277 @safe unittest {
278     const tu = parse(
279         Cpp(
280             q{
281                 template<typename>
282                 struct Struct{};
283 
284                 template<typename T>
285                 struct Struct<T&> {
286                     using Type = T;
287                 };
288             }
289         )
290     );
291 
292     tu.children.length.shouldEqual(2);
293 
294     const general = tu.children[0];
295     const special = tu.children[1];
296 
297     general.kind.should == Cursor.Kind.ClassTemplate;
298     special.kind.should == Cursor.Kind.ClassTemplatePartialSpecialization;
299 
300     special.type.kind.should == Type.Kind.Unexposed;
301     special.type.numTemplateArguments.should == 1;
302     const templateType = special.type.typeTemplateArgument(0);
303 
304     templateType.spelling.should == "type-parameter-0-0 &";
305 }
306 
307 
308 @Tags("contract")
309 @("ParmDecl")
310 @safe unittest {
311     const tu = parse(
312         Cpp(
313             q{
314                 template<typename> struct Struct{};
315                 template<typename R, typename... A>
316                 struct Struct<R(A...)> {};
317             }
318         )
319     );
320 
321     tu.children.length.should == 2;
322     const partial = tu.children[1];
323 
324     partial.kind.should == Cursor.Kind.ClassTemplatePartialSpecialization;
325     printChildren(partial);
326     partial.children.length.should == 4;
327 
328     partial.children[0].kind.should == Cursor.Kind.TemplateTypeParameter;
329     partial.children[0].spelling.should == "R";
330 
331     partial.children[1].kind.should == Cursor.Kind.TemplateTypeParameter;
332     partial.children[1].spelling.should == "A";
333 
334     partial.children[2].kind.should == Cursor.Kind.TypeRef;
335     partial.children[2].spelling.should == "R";
336 
337     partial.children[3].kind.should == Cursor.Kind.ParmDecl;
338     partial.children[3].spelling.should == "";
339 
340     const parmDecl = partial.children[3];
341     parmDecl.type.kind.should == Type.Kind.Unexposed;
342     parmDecl.type.spelling.should == "A...";
343 }
344 
345 @Tags("contract")
346 @("ctor.copy.definition.only")
347 @safe unittest {
348     const tu = parse(
349         Cpp(
350             q{
351                 template<typename T>
352                 struct Struct{
353                     Struct(const Struct& other) {}
354                 };
355             }
356         )
357     );
358 
359     tu.children.length.should == 1;
360 
361     const struct0 = tu.children[0];
362     struct0.kind.should == Cursor.Kind.ClassTemplate;
363     printChildren(struct0);
364     struct0.children.length.should == 2;
365 
366     const templateParam0 = struct0.children[0];
367     printChildren(templateParam0);
368     templateParam0.kind.should == Cursor.Kind.TemplateTypeParameter;
369     // We named it so it shows up as T, not as type-parameter-0-0
370     templateParam0.type.spelling.should == "T";
371 
372     const ctor = struct0.children[1];
373     printChildren(ctor);
374     ctor.kind.should == Cursor.Kind.Constructor;
375 
376     version(Windows)
377         ctor.children.length.should == 1; // Windows llvm ast doesn't include the body...
378     else
379         ctor.children.length.should == 2;
380 
381     const ctorParam = ctor.children[0];
382     ctorParam.kind.should == Cursor.Kind.ParmDecl;
383     ctorParam.type.kind.should == Type.Kind.LValueReference;
384     // The spelling here is different from the other test below
385     ctorParam.type.spelling.should == "const Struct<T> &";
386 }
387 
388 
389 @Tags("contract")
390 @("ctor.copy.definition.declaration")
391 @safe unittest {
392     const tu = parse(
393         Cpp(
394             q{
395                 template <typename> struct Struct;
396 
397                 template<typename T>
398                 struct Struct{
399                     Struct(const Struct& other) {}
400                 };
401             }
402         )
403     );
404 
405     tu.children.length.should == 2;
406 
407     const struct0 = tu.children[0];
408     struct0.kind.should == Cursor.Kind.ClassTemplate;
409     printChildren(struct0);
410     struct0.children.length.should == 1;
411 
412     const templateParam0 = struct0.children[0];
413     templateParam0.kind.should == Cursor.Kind.TemplateTypeParameter;
414     templateParam0.type.spelling.should == "type-parameter-0-0";
415 
416     const struct1 = tu.children[1];
417     struct1.kind.should == Cursor.Kind.ClassTemplate;
418     printChildren(struct1);
419     struct1.children.length.should == 2;
420 
421     const templateParam1 = struct1.children[0];
422     printChildren(templateParam1);
423     templateParam1.kind.should == Cursor.Kind.TemplateTypeParameter;
424     templateParam1.type.spelling.should == "T";
425 
426     const ctor = struct1.children[1];
427     printChildren(ctor);
428     ctor.kind.should == Cursor.Kind.Constructor;
429     ctor.templateParams.length.should == 0;  // not a template function
430     ctor.semanticParent.templateParams.length.should == 1;  // the `T`
431     ctor.semanticParent.templateParams[0].spelling.should == "T";
432 
433     version(Windows)
434         ctor.children.length.should == 1; // Windows llvm ast doesn't include the body...
435     else
436         ctor.children.length.should == 2;
437     const ctorParam = ctor.children[0];
438     ctorParam.kind.should == Cursor.Kind.ParmDecl;
439     ctorParam.type.kind.should == Type.Kind.LValueReference;
440 
441     // The spelling here is different from the other test above.
442     // The class template type paramater is spelled "T" but the _same_
443     // generic type as a part of a function parameter list gets spelled
444     // "type-parameter-0-0" just because the original definition left out
445     // the type parameter name.
446 
447     ctorParam.type.spelling.should == "const Struct<type-parameter-0-0> &";
448     ctorParam.type.canonical.spelling.should == "const Struct<type-parameter-0-0> &";
449 }
450 
451 
452 @Tags("contract")
453 @("pointer to T")
454 @safe unittest {
455     const tu = parse(
456         Cpp(
457             q{
458                 template<typename T>
459                 struct Struct {
460                     T* data;
461                 };
462             }
463         )
464     );
465 
466     tu.children.length.should == 1;
467     const struct_ = tu.children[0];
468     printChildren(struct_);
469 
470     struct_.kind.should == Cursor.Kind.ClassTemplate;
471     struct_.spelling.should == "Struct";
472     struct_.children.length.should == 2;
473 
474     struct_.children[0].kind.should == Cursor.Kind.TemplateTypeParameter;
475     struct_.children[0].spelling.should == "T";
476 
477     const data = struct_.children[1];
478     data.kind.should == Cursor.Kind.FieldDecl;
479     data.spelling.should == "data";
480     data.type.kind.should == Type.Kind.Pointer;
481     data.type.spelling.should == "T *";
482     data.type.pointee.kind.should == Type.Kind.Unexposed;
483     data.type.pointee.spelling.should == "T";
484 }
485 
486 @Tags("contract")
487 @("enum")
488 @safe unittest {
489     const tu = parse(
490         Cpp(
491             q{
492                 template<int I> struct Struct { enum { value = I }; };
493             }
494         )
495     );
496 
497     tu.children.length.should == 1;
498     const template_ = tu.children[0];
499     printChildren(template_);
500 
501     template_.kind.should == Cursor.Kind.ClassTemplate;
502     template_.children.length.should == 2;
503     template_.children[0].kind.should == Cursor.Kind.NonTypeTemplateParameter;
504 
505     const enumDecl = template_.children[1];
506     enumDecl.kind.should == Cursor.Kind.EnumDecl;
507     printChildren(enumDecl);
508 
509     enumDecl.children.length.should == 1;
510     const enumConstantDecl = enumDecl.children[0];
511     enumConstantDecl.kind.should == Cursor.Kind.EnumConstantDecl;
512     enumConstantDecl.spelling.should == "value";
513     enumConstantDecl.enumConstantValue.should == 0;  // it's a template
514     writelnUt(enumConstantDecl.tokens);
515 }
516 
517 
518 @Tags("contract")
519 @("value template argument specialisation")
520 @safe unittest {
521 
522     import clang: Token;
523 
524     const tu = parse(
525         Cpp(
526             q{
527                 template<int I> struct Struct { enum { value = I }; };
528                 template<> struct Struct<42> { using Type = void; };
529             }
530         )
531     );
532 
533     tu.children.length.should == 2;
534     const template_ = tu.children[0];
535     template_.kind.should == Cursor.Kind.ClassTemplate;
536     const struct_ = tu.children[1];
537     struct_.kind.should == Cursor.Kind.StructDecl;
538     printChildren(struct_);
539 
540     struct_.children.length.should == 2;
541     const integerLiteral = struct_.children[0];
542     integerLiteral.kind.should == Cursor.Kind.IntegerLiteral;
543     integerLiteral.spelling.should == "";
544 
545     integerLiteral.tokens.should == [Token(Token.Kind.Literal, "42")];
546 }
547 
548 
549 @Tags("contract")
550 @("using.partial")
551 @safe unittest {
552 
553     const tu = parse(
554         Cpp(
555             q{
556                 template<typename T>
557                 struct new_allocator {
558                 };
559 
560                 template<typename _Tp>
561                 using __allocator_base = new_allocator<_Tp>;
562             }
563         )
564     );
565 
566     tu.children.length.should == 2;
567 
568     const using = tu.child(1);
569     using.kind.should == Cursor.Kind.TypeAliasTemplateDecl;
570     using.spelling.should == "__allocator_base";
571     using.type.kind.should == Type.Kind.Invalid;
572     using.type.spelling.should == "";
573 
574     printChildren(using);
575     using.children.length.should == 2;
576 
577     const typeParam = using.child(0);
578     typeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
579     typeParam.spelling.should == "_Tp";
580     typeParam.type.kind.should == Type.Kind.Unexposed;
581     typeParam.spelling.should == "_Tp";
582     printChildren(typeParam);
583     typeParam.children.length.should == 0;
584 
585     const typeAlias = using.child(1);
586     typeAlias.kind.should == Cursor.Kind.TypeAliasDecl;
587     typeAlias.spelling.should == "__allocator_base";
588     typeAlias.type.kind.should == Type.Kind.Typedef;
589     typeAlias.type.spelling.should == "__allocator_base";
590     typeAlias.underlyingType.kind.should == Type.Kind.Unexposed;
591     typeAlias.underlyingType.spelling.should == "new_allocator<_Tp>";
592     typeAlias.underlyingType.canonical.kind.should == Type.Kind.Unexposed;
593     typeAlias.underlyingType.canonical.spelling.should == "new_allocator<type-parameter-0-0>";
594     printChildren(typeAlias);
595     typeAlias.children.length.should == 2;
596 
597     const templateRef = typeAlias.child(0);
598     templateRef.kind.should == Cursor.Kind.TemplateRef;
599     templateRef.spelling.should == "new_allocator";
600     templateRef.type.kind.should == Type.Kind.Invalid;
601     templateRef.type.spelling.should == "";
602     printChildren(templateRef);
603     templateRef.children.length.should == 0;
604 
605     const typeRef = typeAlias.child(1);
606     typeRef.kind.should == Cursor.Kind.TypeRef;
607     typeRef.spelling.should == "_Tp";
608     typeRef.type.kind.should == Type.Kind.Unexposed;
609     typeRef.type.spelling.should == "_Tp";
610     printChildren(typeRef);
611     typeRef.children.length.should == 0;
612 }
613 
614 
615 @Tags("contract")
616 @("using.complete")
617 @safe unittest {
618 
619     const tu = parse(
620         Cpp(
621             q{
622                 template<typename...> using __void_t = void;
623             }
624         )
625     );
626 
627     tu.children.length.should == 1;
628 
629     const using = tu.child(0);
630     using.kind.should == Cursor.Kind.TypeAliasTemplateDecl;
631     using.spelling.should == "__void_t";
632     using.type.kind.should == Type.Kind.Invalid;
633     using.type.spelling.should == "";
634     using.underlyingType.kind.should == Type.Kind.Invalid;
635     printChildren(using);
636     using.children.length.should == 2;
637 
638     const templateTypeParam = using.child(0);
639     templateTypeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
640     templateTypeParam.spelling.should == "";
641     templateTypeParam.type.kind.should == Type.Kind.Unexposed;
642     templateTypeParam.type.spelling.should == "type-parameter-0-0";
643 
644     const typeAlias = using.child(1);
645     typeAlias.kind.should == Cursor.Kind.TypeAliasDecl;
646     typeAlias.spelling.should == "__void_t";
647     typeAlias.type.kind.should == Type.Kind.Typedef;
648     typeAlias.type.spelling.should == "__void_t";
649     typeAlias.underlyingType.kind.should == Type.Kind.Void;
650     typeAlias.underlyingType.spelling.should == "void";
651 }
652 
653 
654 @Tags("contract")
655 @("function.equals")
656 @safe unittest {
657 
658     const tu = parse(
659         Cpp(
660             q{
661                 struct Foo {
662                     template<typename T0, typename T1>
663                     bool equals(T0 lhs, T1 rhs);
664                 };
665             }
666         )
667     );
668 
669     tu.children.length.should == 1;
670     const struct_ = tu.child(0);
671 
672     struct_.shouldMatch(Cursor.Kind.StructDecl, "Foo");
673     printChildren(struct_);
674     struct_.children.length.should == 1;
675 
676     const func = struct_.child(0);
677     func.shouldMatch(Cursor.Kind.FunctionTemplate, "equals");
678     func.type.shouldMatch(Type.Kind.FunctionProto, "bool (T0, T1)");
679     printChildren(func);
680     func.children.length.should == 4;
681 
682     const t0 = func.child(0);
683     t0.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T0");
684     t0.type.shouldMatch(Type.Kind.Unexposed, "T0");
685     t0.children.length.should == 0;
686 
687     const t1 = func.child(1);
688     t1.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T1");
689     t1.type.shouldMatch(Type.Kind.Unexposed, "T1");
690     t1.children.length.should == 0;
691 
692     const lhs = func.child(2);
693     lhs.shouldMatch(Cursor.Kind.ParmDecl, "lhs");
694     lhs.type.shouldMatch(Type.Kind.Unexposed, "T0");
695     printChildren(lhs);
696     lhs.children.length.should == 1;
697     const typeRef0 = lhs.child(0);
698     typeRef0.shouldMatch(Cursor.Kind.TypeRef, "T0");
699     typeRef0.type.shouldMatch(Type.Kind.Unexposed,"T0");
700 
701     const rhs = func.child(3);
702     rhs.shouldMatch(Cursor.Kind.ParmDecl, "rhs");
703     rhs.type.shouldMatch(Type.Kind.Unexposed, "T1");
704     printChildren(rhs);
705     rhs.children.length.should == 1;
706     const typeRef1 = rhs.child(0);
707     typeRef1.shouldMatch(Cursor.Kind.TypeRef, "T1");
708     typeRef1.type.shouldMatch(Type.Kind.Unexposed,"T1");
709 }
710 
711 
712 @Tags("contract")
713 @("function.ctor")
714 @safe unittest {
715 
716     const tu = parse(
717         Cpp(
718             q{
719                 template<typename T>
720                 struct Foo {
721                     template<typename U>
722                     Foo(const Foo<U>& other);
723                 };
724             }
725         )
726     );
727 
728     tu.children.length.should == 1;
729     const struct_ = tu.child(0);
730 
731     struct_.shouldMatch(Cursor.Kind.ClassTemplate, "Foo");
732     printChildren(struct_);
733     struct_.children.length.should == 2;
734 
735     const T = struct_.child(0);
736     T.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T");
737     printChildren(T);
738     T.children.length.should == 0;
739 
740     const ctor = struct_.child(1);
741     // being a template makes it not be a Cursor.Kind.Constructor
742     ctor.shouldMatch(Cursor.Kind.FunctionTemplate, "Foo<T>");
743     ctor.type.shouldMatch(Type.Kind.FunctionProto, "void (const Foo<U> &)");
744     printChildren(ctor);
745     ctor.children.length.should == 2;
746 
747     const U = ctor.child(0);
748     U.shouldMatch(Cursor.Kind.TemplateTypeParameter, "U");
749     U.type.shouldMatch(Type.Kind.Unexposed, "U");
750     printChildren(U);
751     U.children.length.should == 0;
752 
753     const other = ctor.child(1);
754     other.shouldMatch(Cursor.Kind.ParmDecl, "other");
755     other.type.shouldMatch(Type.Kind.LValueReference, "const Foo<U> &");
756     other.type.isConstQualified.should == false;
757     other.type.pointee.shouldMatch(Type.Kind.Unexposed, "const Foo<U>");
758     other.type.pointee.isConstQualified.should == true;
759 }
760 
761 
762 @Tags("contract")
763 @("functionproto")
764 @safe unittest {
765     import std.array: array;
766 
767     const tu = parse(
768         Cpp(
769             q{
770                 template<typename T>
771                 struct Template {};
772 
773                 void foo(const Template<double(int)>& arg0);
774             }
775         )
776     );
777 
778     tu.children.length.should == 2;
779 
780     const foo = tu.child(1);
781     foo.shouldMatch(Cursor.Kind.FunctionDecl, "foo");
782     foo.type.shouldMatch(Type.Kind.FunctionProto, "void (const Template<double (int)> &)");
783 
784     const fooParams = foo.type.paramTypes.array;
785     fooParams.length.should == 1;
786 
787     const arg0 = fooParams[0];
788     writelnUt("arg0: ", arg0);
789     arg0.shouldMatch(Type.Kind.LValueReference, "const Template<double (int)> &");
790 
791     const unexposed = arg0.pointee;
792     writelnUt("unexposed: ", unexposed);
793     unexposed.shouldMatch(Type.Kind.Unexposed, "const Template<double (int)>");
794 
795     const record = unexposed.canonical;
796     writelnUt("record: ", record);
797     record.shouldMatch(Type.Kind.Record, "const Template<double (int)>");
798     record.numTemplateArguments.should == 1;
799 
800     const functionProto = record.typeTemplateArgument(0);
801     writelnUt("function proto: ", functionProto);
802     functionProto.shouldMatch(Type.Kind.FunctionProto, "double (int)");
803 
804     const functionProtoParams = functionProto.paramTypes.array;
805     writelnUt("functionProtoParams: ", functionProtoParams);
806     functionProtoParams.length.should == 1;
807     const int_ = functionProtoParams[0];
808     int_.shouldMatch(Type.Kind.Int, "int");
809 
810     const functionProtoReturn = functionProto.returnType;
811     writelnUt("functionProtoReturn: ", functionProtoReturn);
812     functionProtoReturn.shouldMatch(Type.Kind.Double, "double");
813 
814     writelnUt("functionProto pointee: ", functionProto.pointee);
815 }