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     ctor.children.length.should == 2;
377     const ctorParam = ctor.children[0];
378     ctorParam.kind.should == Cursor.Kind.ParmDecl;
379     ctorParam.type.kind.should == Type.Kind.LValueReference;
380     // The spelling here is different from the other test below
381     ctorParam.type.spelling.should == "const Struct<T> &";
382 }
383 
384 
385 @Tags("contract")
386 @("ctor.copy.definition.declaration")
387 @safe unittest {
388     const tu = parse(
389         Cpp(
390             q{
391                 template <typename> struct Struct;
392 
393                 template<typename T>
394                 struct Struct{
395                     Struct(const Struct& other) {}
396                 };
397             }
398         )
399     );
400 
401     tu.children.length.should == 2;
402 
403     const struct0 = tu.children[0];
404     struct0.kind.should == Cursor.Kind.ClassTemplate;
405     printChildren(struct0);
406     struct0.children.length.should == 1;
407 
408     const templateParam0 = struct0.children[0];
409     templateParam0.kind.should == Cursor.Kind.TemplateTypeParameter;
410     templateParam0.type.spelling.should == "type-parameter-0-0";
411 
412     const struct1 = tu.children[1];
413     struct1.kind.should == Cursor.Kind.ClassTemplate;
414     printChildren(struct1);
415     struct1.children.length.should == 2;
416 
417     const templateParam1 = struct1.children[0];
418     printChildren(templateParam1);
419     templateParam1.kind.should == Cursor.Kind.TemplateTypeParameter;
420     templateParam1.type.spelling.should == "T";
421 
422     const ctor = struct1.children[1];
423     printChildren(ctor);
424     ctor.kind.should == Cursor.Kind.Constructor;
425     ctor.templateParams.length.should == 0;  // not a template function
426     ctor.semanticParent.templateParams.length.should == 1;  // the `T`
427     ctor.semanticParent.templateParams[0].spelling.should == "T";
428 
429     ctor.children.length.should == 2;
430     const ctorParam = ctor.children[0];
431     ctorParam.kind.should == Cursor.Kind.ParmDecl;
432     ctorParam.type.kind.should == Type.Kind.LValueReference;
433 
434     // The spelling here is different from the other test above.
435     // The class template type paramater is spelled "T" but the _same_
436     // generic type as a part of a function parameter list gets spelled
437     // "type-parameter-0-0" just because the original definition left out
438     // the type parameter name.
439 
440     ctorParam.type.spelling.should == "const Struct<type-parameter-0-0> &";
441     ctorParam.type.canonical.spelling.should == "const Struct<type-parameter-0-0> &";
442 }
443 
444 
445 @Tags("contract")
446 @("pointer to T")
447 @safe unittest {
448     const tu = parse(
449         Cpp(
450             q{
451                 template<typename T>
452                 struct Struct {
453                     T* data;
454                 };
455             }
456         )
457     );
458 
459     tu.children.length.should == 1;
460     const struct_ = tu.children[0];
461     printChildren(struct_);
462 
463     struct_.kind.should == Cursor.Kind.ClassTemplate;
464     struct_.spelling.should == "Struct";
465     struct_.children.length.should == 2;
466 
467     struct_.children[0].kind.should == Cursor.Kind.TemplateTypeParameter;
468     struct_.children[0].spelling.should == "T";
469 
470     const data = struct_.children[1];
471     data.kind.should == Cursor.Kind.FieldDecl;
472     data.spelling.should == "data";
473     data.type.kind.should == Type.Kind.Pointer;
474     data.type.spelling.should == "T *";
475     data.type.pointee.kind.should == Type.Kind.Unexposed;
476     data.type.pointee.spelling.should == "T";
477 }
478 
479 @Tags("contract")
480 @("enum")
481 @safe unittest {
482     const tu = parse(
483         Cpp(
484             q{
485                 template<int I> struct Struct { enum { value = I }; };
486             }
487         )
488     );
489 
490     tu.children.length.should == 1;
491     const template_ = tu.children[0];
492     printChildren(template_);
493 
494     template_.kind.should == Cursor.Kind.ClassTemplate;
495     template_.children.length.should == 2;
496     template_.children[0].kind.should == Cursor.Kind.NonTypeTemplateParameter;
497 
498     const enumDecl = template_.children[1];
499     enumDecl.kind.should == Cursor.Kind.EnumDecl;
500     printChildren(enumDecl);
501 
502     enumDecl.children.length.should == 1;
503     const enumConstantDecl = enumDecl.children[0];
504     enumConstantDecl.kind.should == Cursor.Kind.EnumConstantDecl;
505     enumConstantDecl.spelling.should == "value";
506     enumConstantDecl.enumConstantValue.should == 0;  // it's a template
507     writelnUt(enumConstantDecl.tokens);
508 }
509 
510 
511 @Tags("contract")
512 @("value template argument specialisation")
513 @safe unittest {
514 
515     import clang: Token;
516 
517     const tu = parse(
518         Cpp(
519             q{
520                 template<int I> struct Struct { enum { value = I }; };
521                 template<> struct Struct<42> { using Type = void; };
522             }
523         )
524     );
525 
526     tu.children.length.should == 2;
527     const template_ = tu.children[0];
528     template_.kind.should == Cursor.Kind.ClassTemplate;
529     const struct_ = tu.children[1];
530     struct_.kind.should == Cursor.Kind.StructDecl;
531     printChildren(struct_);
532 
533     struct_.children.length.should == 2;
534     const integerLiteral = struct_.children[0];
535     integerLiteral.kind.should == Cursor.Kind.IntegerLiteral;
536     integerLiteral.spelling.should == "";
537 
538     integerLiteral.tokens.should == [Token(Token.Kind.Literal, "42")];
539 }
540 
541 
542 @Tags("contract")
543 @("using.partial")
544 @safe unittest {
545 
546     const tu = parse(
547         Cpp(
548             q{
549                 template<typename T>
550                 struct new_allocator {
551                 };
552 
553                 template<typename _Tp>
554                 using __allocator_base = new_allocator<_Tp>;
555             }
556         )
557     );
558 
559     tu.children.length.should == 2;
560 
561     const using = tu.child(1);
562     using.kind.should == Cursor.Kind.TypeAliasTemplateDecl;
563     using.spelling.should == "__allocator_base";
564     using.type.kind.should == Type.Kind.Invalid;
565     using.type.spelling.should == "";
566 
567     printChildren(using);
568     using.children.length.should == 2;
569 
570     const typeParam = using.child(0);
571     typeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
572     typeParam.spelling.should == "_Tp";
573     typeParam.type.kind.should == Type.Kind.Unexposed;
574     typeParam.spelling.should == "_Tp";
575     printChildren(typeParam);
576     typeParam.children.length.should == 0;
577 
578     const typeAlias = using.child(1);
579     typeAlias.kind.should == Cursor.Kind.TypeAliasDecl;
580     typeAlias.spelling.should == "__allocator_base";
581     typeAlias.type.kind.should == Type.Kind.Typedef;
582     typeAlias.type.spelling.should == "__allocator_base";
583     typeAlias.underlyingType.kind.should == Type.Kind.Unexposed;
584     typeAlias.underlyingType.spelling.should == "new_allocator<_Tp>";
585     typeAlias.underlyingType.canonical.kind.should == Type.Kind.Unexposed;
586     typeAlias.underlyingType.canonical.spelling.should == "new_allocator<type-parameter-0-0>";
587     printChildren(typeAlias);
588     typeAlias.children.length.should == 2;
589 
590     const templateRef = typeAlias.child(0);
591     templateRef.kind.should == Cursor.Kind.TemplateRef;
592     templateRef.spelling.should == "new_allocator";
593     templateRef.type.kind.should == Type.Kind.Invalid;
594     templateRef.type.spelling.should == "";
595     printChildren(templateRef);
596     templateRef.children.length.should == 0;
597 
598     const typeRef = typeAlias.child(1);
599     typeRef.kind.should == Cursor.Kind.TypeRef;
600     typeRef.spelling.should == "_Tp";
601     typeRef.type.kind.should == Type.Kind.Unexposed;
602     typeRef.type.spelling.should == "_Tp";
603     printChildren(typeRef);
604     typeRef.children.length.should == 0;
605 }
606 
607 
608 @Tags("contract")
609 @("using.complete")
610 @safe unittest {
611 
612     const tu = parse(
613         Cpp(
614             q{
615                 template<typename...> using __void_t = void;
616             }
617         )
618     );
619 
620     tu.children.length.should == 1;
621 
622     const using = tu.child(0);
623     using.kind.should == Cursor.Kind.TypeAliasTemplateDecl;
624     using.spelling.should == "__void_t";
625     using.type.kind.should == Type.Kind.Invalid;
626     using.type.spelling.should == "";
627     using.underlyingType.kind.should == Type.Kind.Invalid;
628     printChildren(using);
629     using.children.length.should == 2;
630 
631     const templateTypeParam = using.child(0);
632     templateTypeParam.kind.should == Cursor.Kind.TemplateTypeParameter;
633     templateTypeParam.spelling.should == "";
634     templateTypeParam.type.kind.should == Type.Kind.Unexposed;
635     templateTypeParam.type.spelling.should == "type-parameter-0-0";
636 
637     const typeAlias = using.child(1);
638     typeAlias.kind.should == Cursor.Kind.TypeAliasDecl;
639     typeAlias.spelling.should == "__void_t";
640     typeAlias.type.kind.should == Type.Kind.Typedef;
641     typeAlias.type.spelling.should == "__void_t";
642     typeAlias.underlyingType.kind.should == Type.Kind.Void;
643     typeAlias.underlyingType.spelling.should == "void";
644 }
645 
646 
647 @Tags("contract")
648 @("function.equals")
649 @safe unittest {
650 
651     const tu = parse(
652         Cpp(
653             q{
654                 struct Foo {
655                     template<typename T0, typename T1>
656                     bool equals(T0 lhs, T1 rhs);
657                 };
658             }
659         )
660     );
661 
662     tu.children.length.should == 1;
663     const struct_ = tu.child(0);
664 
665     struct_.shouldMatch(Cursor.Kind.StructDecl, "Foo");
666     printChildren(struct_);
667     struct_.children.length.should == 1;
668 
669     const func = struct_.child(0);
670     func.shouldMatch(Cursor.Kind.FunctionTemplate, "equals");
671     func.type.shouldMatch(Type.Kind.FunctionProto, "bool (T0, T1)");
672     printChildren(func);
673     func.children.length.should == 4;
674 
675     const t0 = func.child(0);
676     t0.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T0");
677     t0.type.shouldMatch(Type.Kind.Unexposed, "T0");
678     t0.children.length.should == 0;
679 
680     const t1 = func.child(1);
681     t1.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T1");
682     t1.type.shouldMatch(Type.Kind.Unexposed, "T1");
683     t1.children.length.should == 0;
684 
685     const lhs = func.child(2);
686     lhs.shouldMatch(Cursor.Kind.ParmDecl, "lhs");
687     lhs.type.shouldMatch(Type.Kind.Unexposed, "T0");
688     printChildren(lhs);
689     lhs.children.length.should == 1;
690     const typeRef0 = lhs.child(0);
691     typeRef0.shouldMatch(Cursor.Kind.TypeRef, "T0");
692     typeRef0.type.shouldMatch(Type.Kind.Unexposed,"T0");
693 
694     const rhs = func.child(3);
695     rhs.shouldMatch(Cursor.Kind.ParmDecl, "rhs");
696     rhs.type.shouldMatch(Type.Kind.Unexposed, "T1");
697     printChildren(rhs);
698     rhs.children.length.should == 1;
699     const typeRef1 = rhs.child(0);
700     typeRef1.shouldMatch(Cursor.Kind.TypeRef, "T1");
701     typeRef1.type.shouldMatch(Type.Kind.Unexposed,"T1");
702 }
703 
704 
705 @Tags("contract")
706 @("function.ctor")
707 @safe unittest {
708 
709     const tu = parse(
710         Cpp(
711             q{
712                 template<typename T>
713                 struct Foo {
714                     template<typename U>
715                     Foo(const Foo<U>& other);
716                 };
717             }
718         )
719     );
720 
721     tu.children.length.should == 1;
722     const struct_ = tu.child(0);
723 
724     struct_.shouldMatch(Cursor.Kind.ClassTemplate, "Foo");
725     printChildren(struct_);
726     struct_.children.length.should == 2;
727 
728     const T = struct_.child(0);
729     T.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T");
730     printChildren(T);
731     T.children.length.should == 0;
732 
733     const ctor = struct_.child(1);
734     // being a template makes it not be a Cursor.Kind.Constructor
735     ctor.shouldMatch(Cursor.Kind.FunctionTemplate, "Foo<T>");
736     ctor.type.shouldMatch(Type.Kind.FunctionProto, "void (const Foo<U> &)");
737     printChildren(ctor);
738     ctor.children.length.should == 2;
739 
740     const U = ctor.child(0);
741     U.shouldMatch(Cursor.Kind.TemplateTypeParameter, "U");
742     U.type.shouldMatch(Type.Kind.Unexposed, "U");
743     printChildren(U);
744     U.children.length.should == 0;
745 
746     const other = ctor.child(1);
747     other.shouldMatch(Cursor.Kind.ParmDecl, "other");
748     other.type.shouldMatch(Type.Kind.LValueReference, "const Foo<U> &");
749     other.type.isConstQualified.should == false;
750     other.type.pointee.shouldMatch(Type.Kind.Unexposed, "const Foo<U>");
751     other.type.pointee.isConstQualified.should == true;
752 }
753 
754 
755 @Tags("contract")
756 @("functionproto")
757 @safe unittest {
758     import std.array: array;
759 
760     const tu = parse(
761         Cpp(
762             q{
763                 template<typename T>
764                 struct Template {};
765 
766                 void foo(const Template<double(int)>& arg0);
767             }
768         )
769     );
770 
771     tu.children.length.should == 2;
772 
773     const foo = tu.child(1);
774     foo.shouldMatch(Cursor.Kind.FunctionDecl, "foo");
775     foo.type.shouldMatch(Type.Kind.FunctionProto, "void (const Template<double (int)> &)");
776 
777     const fooParams = foo.type.paramTypes.array;
778     fooParams.length.should == 1;
779 
780     const arg0 = fooParams[0];
781     writelnUt("arg0: ", arg0);
782     arg0.shouldMatch(Type.Kind.LValueReference, "const Template<double (int)> &");
783 
784     const unexposed = arg0.pointee;
785     writelnUt("unexposed: ", unexposed);
786     unexposed.shouldMatch(Type.Kind.Unexposed, "const Template<double (int)>");
787 
788     const record = unexposed.canonical;
789     writelnUt("record: ", record);
790     record.shouldMatch(Type.Kind.Record, "const Template<double (int)>");
791     record.numTemplateArguments.should == 1;
792 
793     const functionProto = record.typeTemplateArgument(0);
794     writelnUt("function proto: ", functionProto);
795     functionProto.shouldMatch(Type.Kind.FunctionProto, "double (int)");
796 
797     const functionProtoParams = functionProto.paramTypes.array;
798     writelnUt("functionProtoParams: ", functionProtoParams);
799     functionProtoParams.length.should == 1;
800     const int_ = functionProtoParams[0];
801     int_.shouldMatch(Type.Kind.Int, "int");
802 
803     const functionProtoReturn = functionProto.returnType;
804     writelnUt("functionProtoReturn: ", functionProtoReturn);
805     functionProtoReturn.shouldMatch(Type.Kind.Double, "double");
806 
807     writelnUt("functionProto pointee: ", functionProto.pointee);
808 }