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", "notravis") 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", "notravis") 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 }