| 1 | //===-- clang-doc/SerializeTest.cpp ---------------------------------------===// |
| 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 "Serialize.h" |
| 10 | #include "ClangDocTest.h" |
| 11 | #include "Representation.h" |
| 12 | #include "clang/AST/Comment.h" |
| 13 | #include "clang/AST/RecursiveASTVisitor.h" |
| 14 | #include "gtest/gtest.h" |
| 15 | |
| 16 | namespace clang { |
| 17 | namespace doc { |
| 18 | |
| 19 | class ClangDocSerializeTestVisitor |
| 20 | : public RecursiveASTVisitor<ClangDocSerializeTestVisitor> { |
| 21 | |
| 22 | EmittedInfoList &EmittedInfos; |
| 23 | bool Public; |
| 24 | |
| 25 | comments::FullComment *(const NamedDecl *D) const { |
| 26 | if (RawComment * = |
| 27 | D->getASTContext().getRawCommentForDeclNoCache(D)) { |
| 28 | Comment->setAttached(); |
| 29 | return Comment->parse(Context: D->getASTContext(), PP: nullptr, D); |
| 30 | } |
| 31 | return nullptr; |
| 32 | } |
| 33 | |
| 34 | public: |
| 35 | ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public) |
| 36 | : EmittedInfos(EmittedInfos), Public(Public) {} |
| 37 | |
| 38 | template <typename T> bool mapDecl(const T *D) { |
| 39 | Location Loc(0, 0, "test.cpp" ); |
| 40 | auto [Child, Parent] = serialize::emitInfo(D, getComment(D), Loc, Public); |
| 41 | if (Child) |
| 42 | EmittedInfos.emplace_back(std::move(Child)); |
| 43 | if (Parent) |
| 44 | EmittedInfos.emplace_back(std::move(Parent)); |
| 45 | return true; |
| 46 | } |
| 47 | |
| 48 | bool VisitNamespaceDecl(const NamespaceDecl *D) { return mapDecl(D); } |
| 49 | |
| 50 | bool VisitFunctionDecl(const FunctionDecl *D) { |
| 51 | // Don't visit CXXMethodDecls twice |
| 52 | if (dyn_cast<CXXMethodDecl>(Val: D)) |
| 53 | return true; |
| 54 | return mapDecl(D); |
| 55 | } |
| 56 | |
| 57 | bool VisitCXXMethodDecl(const CXXMethodDecl *D) { return mapDecl(D); } |
| 58 | |
| 59 | bool VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); } |
| 60 | |
| 61 | bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); } |
| 62 | |
| 63 | bool VisitTypedefDecl(const TypedefDecl *D) { return mapDecl(D); } |
| 64 | |
| 65 | bool VisitTypeAliasDecl(const TypeAliasDecl *D) { return mapDecl(D); } |
| 66 | }; |
| 67 | |
| 68 | void (StringRef Code, size_t NumExpectedInfos, bool Public, |
| 69 | EmittedInfoList &EmittedInfos) { |
| 70 | auto ASTUnit = clang::tooling::buildASTFromCode(Code); |
| 71 | auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); |
| 72 | ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); |
| 73 | Visitor.TraverseTranslationUnitDecl(TU); |
| 74 | ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); |
| 75 | } |
| 76 | |
| 77 | void (StringRef Code, size_t NumExpectedInfos, |
| 78 | bool Public, EmittedInfoList &EmittedInfos, |
| 79 | std::vector<std::string> &Args) { |
| 80 | auto ASTUnit = clang::tooling::buildASTFromCodeWithArgs(Code, Args); |
| 81 | auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); |
| 82 | ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); |
| 83 | Visitor.TraverseTranslationUnitDecl(TU); |
| 84 | ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); |
| 85 | } |
| 86 | |
| 87 | // Constructs a comment definition as the parser would for one comment line. |
| 88 | /* TODO uncomment this when the missing comment is fixed in emitRecordInfo and |
| 89 | the code that calls this is re-enabled. |
| 90 | CommentInfo MakeOneLineCommentInfo(const std::string &Text) { |
| 91 | CommentInfo TopComment; |
| 92 | TopComment.Kind = "FullComment"; |
| 93 | TopComment.Children.emplace_back(std::make_unique<CommentInfo>()); |
| 94 | |
| 95 | CommentInfo *Brief = TopComment.Children.back().get(); |
| 96 | Brief->Kind = "ParagraphComment"; |
| 97 | |
| 98 | Brief->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 99 | Brief->Children.back()->Kind = "TextComment"; |
| 100 | Brief->Children.back()->Name = "ParagraphComment"; |
| 101 | Brief->Children.back()->Text = Text; |
| 102 | |
| 103 | return TopComment; |
| 104 | } |
| 105 | */ |
| 106 | |
| 107 | // Test serialization of namespace declarations. |
| 108 | TEST(SerializeTest, emitNamespaceInfo) { |
| 109 | EmittedInfoList Infos; |
| 110 | ExtractInfosFromCode(Code: "namespace A { namespace B { void f() {} } }" , NumExpectedInfos: 5, |
| 111 | /*Public=*/false, EmittedInfos&: Infos); |
| 112 | |
| 113 | NamespaceInfo *A = InfoAsNamespace(I: Infos[0].get()); |
| 114 | NamespaceInfo ExpectedA(EmptySID, "A" ); |
| 115 | CheckNamespaceInfo(Expected: &ExpectedA, Actual: A); |
| 116 | |
| 117 | NamespaceInfo *B = InfoAsNamespace(I: Infos[2].get()); |
| 118 | NamespaceInfo ExpectedB(EmptySID, /*Name=*/"B" , /*Path=*/"A" ); |
| 119 | ExpectedB.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
| 120 | CheckNamespaceInfo(Expected: &ExpectedB, Actual: B); |
| 121 | |
| 122 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[4].get()); |
| 123 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
| 124 | FunctionInfo F; |
| 125 | F.Name = "f" ; |
| 126 | F.ReturnType = TypeInfo("void" ); |
| 127 | F.DefLoc = Location(0, 0, "test.cpp" ); |
| 128 | F.Namespace.emplace_back(Args: EmptySID, Args: "B" , Args: InfoType::IT_namespace); |
| 129 | F.Namespace.emplace_back(Args: EmptySID, Args: "A" , Args: InfoType::IT_namespace); |
| 130 | F.Access = AccessSpecifier::AS_none; |
| 131 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
| 132 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
| 133 | } |
| 134 | |
| 135 | TEST(SerializeTest, emitAnonymousNamespaceInfo) { |
| 136 | EmittedInfoList Infos; |
| 137 | ExtractInfosFromCode(Code: "namespace { }" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
| 138 | |
| 139 | NamespaceInfo *A = InfoAsNamespace(I: Infos[0].get()); |
| 140 | NamespaceInfo ExpectedA(EmptySID); |
| 141 | ExpectedA.Name = "@nonymous_namespace" ; |
| 142 | CheckNamespaceInfo(Expected: &ExpectedA, Actual: A); |
| 143 | } |
| 144 | |
| 145 | // Test serialization of record declarations. |
| 146 | TEST(SerializeTest, emitRecordInfo) { |
| 147 | EmittedInfoList Infos; |
| 148 | ExtractInfosFromCode(Code: R"raw(class E { |
| 149 | public: |
| 150 | E() {} |
| 151 | |
| 152 | // Some docs. |
| 153 | int value; |
| 154 | protected: |
| 155 | void ProtectedMethod(); |
| 156 | }; |
| 157 | template <typename T> |
| 158 | struct F { |
| 159 | void TemplateMethod(); |
| 160 | }; |
| 161 | template <> |
| 162 | void F<int>::TemplateMethod(); |
| 163 | typedef struct {} G;)raw" , |
| 164 | NumExpectedInfos: 10, /*Public=*/false, EmittedInfos&: Infos); |
| 165 | |
| 166 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
| 167 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
| 168 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 169 | Args: InfoType::IT_namespace); |
| 170 | ExpectedE.TagType = TagTypeKind::Class; |
| 171 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
| 172 | ExpectedE.Members.emplace_back(Args: TypeInfo("int" ), Args: "value" , |
| 173 | Args: AccessSpecifier::AS_public); |
| 174 | // TODO the data member should have the docstring on it: |
| 175 | //ExpectedE.Members.back().Description.push_back(MakeOneLineCommentInfo(" Some docs")); |
| 176 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
| 177 | |
| 178 | RecordInfo *RecordWithEConstructor = InfoAsRecord(I: Infos[2].get()); |
| 179 | RecordInfo ExpectedRecordWithEConstructor(EmptySID); |
| 180 | FunctionInfo EConstructor; |
| 181 | EConstructor.Name = "E" ; |
| 182 | EConstructor.Parent = Reference(EmptySID, "E" , InfoType::IT_record); |
| 183 | EConstructor.ReturnType = TypeInfo("void" ); |
| 184 | EConstructor.DefLoc = Location(0, 0, "test.cpp" ); |
| 185 | EConstructor.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
| 186 | EConstructor.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 187 | Args: InfoType::IT_namespace); |
| 188 | EConstructor.Access = AccessSpecifier::AS_public; |
| 189 | EConstructor.IsMethod = true; |
| 190 | ExpectedRecordWithEConstructor.Children.Functions.emplace_back( |
| 191 | args: std::move(EConstructor)); |
| 192 | CheckRecordInfo(Expected: &ExpectedRecordWithEConstructor, Actual: RecordWithEConstructor); |
| 193 | |
| 194 | RecordInfo *RecordWithMethod = InfoAsRecord(I: Infos[3].get()); |
| 195 | RecordInfo ExpectedRecordWithMethod(EmptySID); |
| 196 | FunctionInfo Method; |
| 197 | Method.Name = "ProtectedMethod" ; |
| 198 | Method.Parent = Reference(EmptySID, "E" , InfoType::IT_record); |
| 199 | Method.ReturnType = TypeInfo("void" ); |
| 200 | Method.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 201 | Method.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
| 202 | Method.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 203 | Args: InfoType::IT_namespace); |
| 204 | Method.Access = AccessSpecifier::AS_protected; |
| 205 | Method.IsMethod = true; |
| 206 | ExpectedRecordWithMethod.Children.Functions.emplace_back(args: std::move(Method)); |
| 207 | CheckRecordInfo(Expected: &ExpectedRecordWithMethod, Actual: RecordWithMethod); |
| 208 | |
| 209 | RecordInfo *F = InfoAsRecord(I: Infos[4].get()); |
| 210 | RecordInfo ExpectedF(EmptySID, /*Name=*/"F" , /*Path=*/"GlobalNamespace" ); |
| 211 | ExpectedF.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 212 | Args: InfoType::IT_namespace); |
| 213 | ExpectedF.TagType = TagTypeKind::Struct; |
| 214 | ExpectedF.DefLoc = Location(0, 0, "test.cpp" ); |
| 215 | CheckRecordInfo(Expected: &ExpectedF, Actual: F); |
| 216 | |
| 217 | RecordInfo *RecordWithTemplateMethod = InfoAsRecord(I: Infos[6].get()); |
| 218 | RecordInfo ExpectedRecordWithTemplateMethod(EmptySID); |
| 219 | FunctionInfo TemplateMethod; |
| 220 | TemplateMethod.Name = "TemplateMethod" ; |
| 221 | TemplateMethod.Parent = Reference(EmptySID, "F" , InfoType::IT_record); |
| 222 | TemplateMethod.ReturnType = TypeInfo("void" ); |
| 223 | TemplateMethod.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 224 | TemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
| 225 | TemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 226 | Args: InfoType::IT_namespace); |
| 227 | TemplateMethod.Access = AccessSpecifier::AS_public; |
| 228 | TemplateMethod.IsMethod = true; |
| 229 | ExpectedRecordWithTemplateMethod.Children.Functions.emplace_back( |
| 230 | args: std::move(TemplateMethod)); |
| 231 | CheckRecordInfo(Expected: &ExpectedRecordWithTemplateMethod, Actual: RecordWithTemplateMethod); |
| 232 | |
| 233 | RecordInfo *TemplatedRecord = InfoAsRecord(I: Infos[7].get()); |
| 234 | RecordInfo ExpectedTemplatedRecord(EmptySID); |
| 235 | FunctionInfo SpecializedTemplateMethod; |
| 236 | SpecializedTemplateMethod.Name = "TemplateMethod" ; |
| 237 | SpecializedTemplateMethod.Parent = |
| 238 | Reference(EmptySID, "F" , InfoType::IT_record); |
| 239 | SpecializedTemplateMethod.ReturnType = TypeInfo("void" ); |
| 240 | SpecializedTemplateMethod.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 241 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "F" , |
| 242 | Args: InfoType::IT_record); |
| 243 | SpecializedTemplateMethod.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 244 | Args: InfoType::IT_namespace); |
| 245 | SpecializedTemplateMethod.Access = AccessSpecifier::AS_public; |
| 246 | SpecializedTemplateMethod.IsMethod = true; |
| 247 | ExpectedTemplatedRecord.Children.Functions.emplace_back( |
| 248 | args: std::move(SpecializedTemplateMethod)); |
| 249 | CheckRecordInfo(Expected: &ExpectedTemplatedRecord, Actual: TemplatedRecord); |
| 250 | |
| 251 | RecordInfo *G = InfoAsRecord(I: Infos[8].get()); |
| 252 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
| 253 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 254 | Args: InfoType::IT_namespace); |
| 255 | ExpectedG.TagType = TagTypeKind::Struct; |
| 256 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
| 257 | ExpectedG.IsTypeDef = true; |
| 258 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
| 259 | } |
| 260 | |
| 261 | // Test serialization of enum declarations. |
| 262 | TEST(SerializeTest, emitEnumInfo) { |
| 263 | EmittedInfoList Infos; |
| 264 | ExtractInfosFromCode(Code: "enum E { X, Y }; enum class G { A, B };" , NumExpectedInfos: 2, |
| 265 | /*Public=*/false, EmittedInfos&: Infos); |
| 266 | |
| 267 | NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(I: Infos[0].get()); |
| 268 | NamespaceInfo ExpectedNamespaceWithEnum(EmptySID); |
| 269 | EnumInfo E; |
| 270 | E.Name = "E" ; |
| 271 | E.DefLoc = Location(0, 0, "test.cpp" ); |
| 272 | E.Members.emplace_back(Args: "X" , Args: "0" ); |
| 273 | E.Members.emplace_back(Args: "Y" , Args: "1" ); |
| 274 | ExpectedNamespaceWithEnum.Children.Enums.emplace_back(args: std::move(E)); |
| 275 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithEnum, Actual: NamespaceWithEnum); |
| 276 | |
| 277 | NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(I: Infos[1].get()); |
| 278 | NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID); |
| 279 | EnumInfo G; |
| 280 | G.Name = "G" ; |
| 281 | G.Scoped = true; |
| 282 | G.DefLoc = Location(0, 0, "test.cpp" ); |
| 283 | G.Members.emplace_back(Args: "A" , Args: "0" ); |
| 284 | G.Members.emplace_back(Args: "B" , Args: "1" ); |
| 285 | ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(args: std::move(G)); |
| 286 | CheckNamespaceInfo(Expected: &ExpectedNamespaceWithScopedEnum, Actual: NamespaceWithScopedEnum); |
| 287 | } |
| 288 | |
| 289 | TEST(SerializeTest, emitUndefinedRecordInfo) { |
| 290 | EmittedInfoList Infos; |
| 291 | ExtractInfosFromCode(Code: "class E;" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
| 292 | |
| 293 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
| 294 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
| 295 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 296 | Args: InfoType::IT_namespace); |
| 297 | ExpectedE.TagType = TagTypeKind::Class; |
| 298 | ExpectedE.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 299 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
| 300 | } |
| 301 | |
| 302 | TEST(SerializeTest, emitRecordMemberInfo) { |
| 303 | EmittedInfoList Infos; |
| 304 | ExtractInfosFromCode(Code: "struct E { int I; };" , NumExpectedInfos: 2, /*Public=*/false, EmittedInfos&: Infos); |
| 305 | |
| 306 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
| 307 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
| 308 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 309 | Args: InfoType::IT_namespace); |
| 310 | ExpectedE.TagType = TagTypeKind::Struct; |
| 311 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
| 312 | ExpectedE.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
| 313 | Args: AccessSpecifier::AS_public); |
| 314 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
| 315 | } |
| 316 | |
| 317 | TEST(SerializeTest, emitInternalRecordInfo) { |
| 318 | EmittedInfoList Infos; |
| 319 | ExtractInfosFromCode(Code: "class E { class G {}; };" , NumExpectedInfos: 4, /*Public=*/false, EmittedInfos&: Infos); |
| 320 | |
| 321 | RecordInfo *E = InfoAsRecord(I: Infos[0].get()); |
| 322 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
| 323 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 324 | Args: InfoType::IT_namespace); |
| 325 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
| 326 | ExpectedE.TagType = TagTypeKind::Class; |
| 327 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
| 328 | |
| 329 | RecordInfo *G = InfoAsRecord(I: Infos[2].get()); |
| 330 | llvm::SmallString<128> ExpectedGPath("GlobalNamespace/E" ); |
| 331 | llvm::sys::path::native(path&: ExpectedGPath); |
| 332 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/ExpectedGPath); |
| 333 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
| 334 | ExpectedG.TagType = TagTypeKind::Class; |
| 335 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "E" , Args: InfoType::IT_record); |
| 336 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 337 | Args: InfoType::IT_namespace); |
| 338 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
| 339 | } |
| 340 | |
| 341 | TEST(SerializeTest, emitPublicAnonymousNamespaceInfo) { |
| 342 | EmittedInfoList Infos; |
| 343 | ExtractInfosFromCode(Code: "namespace { class A; }" , NumExpectedInfos: 0, /*Public=*/true, EmittedInfos&: Infos); |
| 344 | } |
| 345 | |
| 346 | TEST(SerializeTest, emitPublicFunctionInternalInfo) { |
| 347 | EmittedInfoList Infos; |
| 348 | ExtractInfosFromCode(Code: "int F() { class G {}; return 0; };" , NumExpectedInfos: 1, /*Public=*/true, |
| 349 | EmittedInfos&: Infos); |
| 350 | |
| 351 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
| 352 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
| 353 | FunctionInfo F; |
| 354 | F.Name = "F" ; |
| 355 | F.ReturnType = TypeInfo("int" ); |
| 356 | F.DefLoc = Location(0, 0, "test.cpp" ); |
| 357 | F.Access = AccessSpecifier::AS_none; |
| 358 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
| 359 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
| 360 | } |
| 361 | |
| 362 | TEST(SerializeTest, emitInlinedFunctionInfo) { |
| 363 | EmittedInfoList Infos; |
| 364 | ExtractInfosFromCode(Code: "inline void F(int I) { };" , NumExpectedInfos: 1, /*Public=*/true, EmittedInfos&: Infos); |
| 365 | |
| 366 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
| 367 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
| 368 | FunctionInfo F; |
| 369 | F.Name = "F" ; |
| 370 | F.ReturnType = TypeInfo("void" ); |
| 371 | F.DefLoc = Location(0, 0, "test.cpp" ); |
| 372 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "I" ); |
| 373 | F.Access = AccessSpecifier::AS_none; |
| 374 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
| 375 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
| 376 | } |
| 377 | |
| 378 | TEST(SerializeTest, emitInheritedRecordInfo) { |
| 379 | EmittedInfoList Infos; |
| 380 | ExtractInfosFromCode(Code: R"raw(class F { protected: void set(int N); }; |
| 381 | class G { public: int get() { return 1; } protected: int I; }; |
| 382 | class E : public F, virtual private G {}; |
| 383 | class H : private E {}; |
| 384 | template <typename T> |
| 385 | class I {} ; |
| 386 | class J : public I<int> {} ;)raw" , |
| 387 | NumExpectedInfos: 14, /*Public=*/false, EmittedInfos&: Infos); |
| 388 | |
| 389 | RecordInfo *F = InfoAsRecord(I: Infos[0].get()); |
| 390 | RecordInfo ExpectedF(EmptySID, /*Name=*/"F" , /*Path=*/"GlobalNamespace" ); |
| 391 | ExpectedF.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 392 | Args: InfoType::IT_namespace, Args: "" ); |
| 393 | ExpectedF.TagType = TagTypeKind::Class; |
| 394 | ExpectedF.DefLoc = Location(0, 0, "test.cpp" ); |
| 395 | CheckRecordInfo(Expected: &ExpectedF, Actual: F); |
| 396 | |
| 397 | RecordInfo *G = InfoAsRecord(I: Infos[3].get()); |
| 398 | RecordInfo ExpectedG(EmptySID, /*Name=*/"G" , /*Path=*/"GlobalNamespace" ); |
| 399 | ExpectedG.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 400 | Args: InfoType::IT_namespace); |
| 401 | ExpectedG.TagType = TagTypeKind::Class; |
| 402 | ExpectedG.DefLoc = Location(0, 0, "test.cpp" ); |
| 403 | ExpectedG.Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
| 404 | Args: AccessSpecifier::AS_protected); |
| 405 | CheckRecordInfo(Expected: &ExpectedG, Actual: G); |
| 406 | |
| 407 | RecordInfo *E = InfoAsRecord(I: Infos[6].get()); |
| 408 | RecordInfo ExpectedE(EmptySID, /*Name=*/"E" , /*Path=*/"GlobalNamespace" ); |
| 409 | ExpectedE.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 410 | Args: InfoType::IT_namespace); |
| 411 | ExpectedE.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "F" , Args: InfoType::IT_record, |
| 412 | /*QualName=*/Args: "" , /*Path*=*/Args: "GlobalNamespace" ); |
| 413 | ExpectedE.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
| 414 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
| 415 | /*Path*=*/Args: "GlobalNamespace" ); |
| 416 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
| 417 | /*Path=*/args: "GlobalNamespace" , args: false, |
| 418 | args: AccessSpecifier::AS_public, args: true); |
| 419 | FunctionInfo FunctionSet; |
| 420 | FunctionSet.Name = "set" ; |
| 421 | FunctionSet.ReturnType = TypeInfo("void" ); |
| 422 | FunctionSet.Loc.emplace_back(); |
| 423 | FunctionSet.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
| 424 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
| 425 | FunctionSet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 426 | Args: InfoType::IT_namespace); |
| 427 | FunctionSet.Access = AccessSpecifier::AS_protected; |
| 428 | FunctionSet.IsMethod = true; |
| 429 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
| 430 | args: std::move(FunctionSet)); |
| 431 | ExpectedE.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
| 432 | /*Path=*/args: "GlobalNamespace" , args: true, |
| 433 | args: AccessSpecifier::AS_private, args: true); |
| 434 | FunctionInfo FunctionGet; |
| 435 | FunctionGet.Name = "get" ; |
| 436 | FunctionGet.ReturnType = TypeInfo("int" ); |
| 437 | FunctionGet.DefLoc = Location(); |
| 438 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
| 439 | FunctionGet.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 440 | Args: InfoType::IT_namespace); |
| 441 | FunctionGet.Access = AccessSpecifier::AS_private; |
| 442 | FunctionGet.IsMethod = true; |
| 443 | ExpectedE.Bases.back().Children.Functions.emplace_back( |
| 444 | args: std::move(FunctionGet)); |
| 445 | ExpectedE.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
| 446 | Args: AccessSpecifier::AS_private); |
| 447 | ExpectedE.DefLoc = Location(0, 0, "test.cpp" ); |
| 448 | ExpectedE.TagType = TagTypeKind::Class; |
| 449 | CheckRecordInfo(Expected: &ExpectedE, Actual: E); |
| 450 | |
| 451 | RecordInfo *H = InfoAsRecord(I: Infos[8].get()); |
| 452 | RecordInfo ExpectedH(EmptySID, /*Name=*/"H" , /*Path=*/"GlobalNamespace" ); |
| 453 | ExpectedH.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 454 | Args: InfoType::IT_namespace); |
| 455 | ExpectedH.TagType = TagTypeKind::Class; |
| 456 | ExpectedH.DefLoc = Location(0, 0, "test.cpp" ); |
| 457 | ExpectedH.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "E" , Args: InfoType::IT_record, |
| 458 | /*QualName=*/Args: "E" , /*Path=*/Args: "GlobalNamespace" ); |
| 459 | ExpectedH.VirtualParents.emplace_back(Args: EmptySID, /*Name=*/Args: "G" , |
| 460 | Args: InfoType::IT_record, /*QualName=*/Args: "G" , |
| 461 | /*Path=*/Args: "GlobalNamespace" ); |
| 462 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "E" , |
| 463 | /*Path=*/args: "GlobalNamespace" , args: false, |
| 464 | args: AccessSpecifier::AS_private, args: true); |
| 465 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "F" , |
| 466 | /*Path=*/args: "GlobalNamespace" , args: false, |
| 467 | args: AccessSpecifier::AS_private, args: false); |
| 468 | FunctionInfo FunctionSetNew; |
| 469 | FunctionSetNew.Name = "set" ; |
| 470 | FunctionSetNew.ReturnType = TypeInfo("void" ); |
| 471 | FunctionSetNew.Loc.emplace_back(); |
| 472 | FunctionSetNew.Params.emplace_back(Args: TypeInfo("int" ), Args: "N" ); |
| 473 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "F" , Args: InfoType::IT_record); |
| 474 | FunctionSetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 475 | Args: InfoType::IT_namespace); |
| 476 | FunctionSetNew.Access = AccessSpecifier::AS_private; |
| 477 | FunctionSetNew.IsMethod = true; |
| 478 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
| 479 | args: std::move(FunctionSetNew)); |
| 480 | ExpectedH.Bases.emplace_back(args: EmptySID, /*Name=*/args: "G" , |
| 481 | /*Path=*/args: "GlobalNamespace" , args: true, |
| 482 | args: AccessSpecifier::AS_private, args: false); |
| 483 | FunctionInfo FunctionGetNew; |
| 484 | FunctionGetNew.Name = "get" ; |
| 485 | FunctionGetNew.ReturnType = TypeInfo("int" ); |
| 486 | FunctionGetNew.DefLoc = Location(); |
| 487 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "G" , Args: InfoType::IT_record); |
| 488 | FunctionGetNew.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 489 | Args: InfoType::IT_namespace); |
| 490 | FunctionGetNew.Access = AccessSpecifier::AS_private; |
| 491 | FunctionGetNew.IsMethod = true; |
| 492 | ExpectedH.Bases.back().Children.Functions.emplace_back( |
| 493 | args: std::move(FunctionGetNew)); |
| 494 | ExpectedH.Bases.back().Members.emplace_back(Args: TypeInfo("int" ), Args: "I" , |
| 495 | Args: AccessSpecifier::AS_private); |
| 496 | CheckRecordInfo(Expected: &ExpectedH, Actual: H); |
| 497 | |
| 498 | RecordInfo *I = InfoAsRecord(I: Infos[10].get()); |
| 499 | RecordInfo ExpectedI(EmptySID, /*Name=*/"I" , /*Path=*/"GlobalNamespace" ); |
| 500 | ExpectedI.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 501 | Args: InfoType::IT_namespace); |
| 502 | ExpectedI.TagType = TagTypeKind::Class; |
| 503 | ExpectedI.DefLoc = Location(0, 0, "test.cpp" ); |
| 504 | CheckRecordInfo(Expected: &ExpectedI, Actual: I); |
| 505 | |
| 506 | RecordInfo *J = InfoAsRecord(I: Infos[12].get()); |
| 507 | RecordInfo ExpectedJ(EmptySID, /*Name=*/"J" , /*Path=*/"GlobalNamespace" ); |
| 508 | ExpectedJ.Namespace.emplace_back(Args: EmptySID, Args: "GlobalNamespace" , |
| 509 | Args: InfoType::IT_namespace); |
| 510 | ExpectedJ.Parents.emplace_back(Args: EmptySID, /*Name=*/Args: "I<int>" , |
| 511 | Args: InfoType::IT_record); |
| 512 | ExpectedJ.Bases.emplace_back(args: EmptySID, /*Name=*/args: "I<int>" , |
| 513 | /*Path=*/args: "GlobalNamespace" , args: false, |
| 514 | args: AccessSpecifier::AS_public, args: true); |
| 515 | ExpectedJ.DefLoc = Location(0, 0, "test.cpp" ); |
| 516 | ExpectedJ.TagType = TagTypeKind::Class; |
| 517 | CheckRecordInfo(Expected: &ExpectedJ, Actual: J); |
| 518 | } |
| 519 | |
| 520 | TEST(SerializeTest, emitModulePublicLFunctions) { |
| 521 | EmittedInfoList Infos; |
| 522 | std::vector<std::string> Args; |
| 523 | Args.push_back(x: "-fmodules-ts" ); |
| 524 | ExtractInfosFromCodeWithArgs(Code: R"raw(export module M; |
| 525 | int moduleFunction(int x, double d = 3.2 - 1.0); |
| 526 | static int staticModuleFunction(int x); |
| 527 | export double exportedModuleFunction(double y);)raw" , |
| 528 | NumExpectedInfos: 2, /*Public=*/true, EmittedInfos&: Infos, Args); |
| 529 | |
| 530 | NamespaceInfo *BWithFunction = InfoAsNamespace(I: Infos[0].get()); |
| 531 | NamespaceInfo ExpectedBWithFunction(EmptySID); |
| 532 | FunctionInfo F; |
| 533 | F.Name = "moduleFunction" ; |
| 534 | F.ReturnType = TypeInfo("int" ); |
| 535 | F.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 536 | F.Params.emplace_back(Args: TypeInfo("int" ), Args: "x" ); |
| 537 | F.Params.emplace_back(Args: TypeInfo("double" ), Args: "d" ); |
| 538 | F.Params.back().DefaultValue = "3.2 - 1.0" ; |
| 539 | F.Access = AccessSpecifier::AS_none; |
| 540 | ExpectedBWithFunction.Children.Functions.emplace_back(args: std::move(F)); |
| 541 | CheckNamespaceInfo(Expected: &ExpectedBWithFunction, Actual: BWithFunction); |
| 542 | |
| 543 | NamespaceInfo *BWithExportedFunction = InfoAsNamespace(I: Infos[1].get()); |
| 544 | NamespaceInfo ExpectedBWithExportedFunction(EmptySID); |
| 545 | FunctionInfo ExportedF; |
| 546 | ExportedF.Name = "exportedModuleFunction" ; |
| 547 | ExportedF.ReturnType = |
| 548 | TypeInfo(Reference(EmptySID, "double" , InfoType::IT_default)); |
| 549 | ExportedF.Loc.emplace_back(Args: 0, Args: 0, Args: "test.cpp" ); |
| 550 | ExportedF.Params.emplace_back(Args: TypeInfo("double" ), Args: "y" ); |
| 551 | ExportedF.Access = AccessSpecifier::AS_none; |
| 552 | ExpectedBWithExportedFunction.Children.Functions.emplace_back( |
| 553 | args: std::move(ExportedF)); |
| 554 | CheckNamespaceInfo(Expected: &ExpectedBWithExportedFunction, Actual: BWithExportedFunction); |
| 555 | } |
| 556 | |
| 557 | // Test serialization of child records in namespaces and other records |
| 558 | TEST(SerializeTest, emitChildRecords) { |
| 559 | EmittedInfoList Infos; |
| 560 | ExtractInfosFromCode(Code: "class A { class B {}; }; namespace { class C {}; } " , NumExpectedInfos: 8, |
| 561 | /*Public=*/false, EmittedInfos&: Infos); |
| 562 | |
| 563 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
| 564 | NamespaceInfo ExpectedParentA(EmptySID); |
| 565 | ExpectedParentA.Children.Records.emplace_back( |
| 566 | args: EmptySID, args: "A" , args: InfoType::IT_record, args: "A" , args: "GlobalNamespace" ); |
| 567 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
| 568 | |
| 569 | RecordInfo *ParentB = InfoAsRecord(I: Infos[3].get()); |
| 570 | RecordInfo ExpectedParentB(EmptySID); |
| 571 | llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A" ); |
| 572 | llvm::sys::path::native(path&: ExpectedParentBPath); |
| 573 | ExpectedParentB.Children.Records.emplace_back( |
| 574 | args: EmptySID, args: "B" , args: InfoType::IT_record, args: "A::B" , args&: ExpectedParentBPath); |
| 575 | CheckRecordInfo(Expected: &ExpectedParentB, Actual: ParentB); |
| 576 | |
| 577 | NamespaceInfo *ParentC = InfoAsNamespace(I: Infos[7].get()); |
| 578 | NamespaceInfo ExpectedParentC(EmptySID); |
| 579 | ExpectedParentC.Children.Records.emplace_back( |
| 580 | args: EmptySID, args: "C" , args: InfoType::IT_record, args: "C" , args: "@nonymous_namespace" ); |
| 581 | CheckNamespaceInfo(Expected: &ExpectedParentC, Actual: ParentC); |
| 582 | } |
| 583 | |
| 584 | // Test serialization of child namespaces |
| 585 | TEST(SerializeTest, emitChildNamespaces) { |
| 586 | EmittedInfoList Infos; |
| 587 | ExtractInfosFromCode(Code: "namespace A { namespace B { } }" , NumExpectedInfos: 4, /*Public=*/false, |
| 588 | EmittedInfos&: Infos); |
| 589 | |
| 590 | NamespaceInfo *ParentA = InfoAsNamespace(I: Infos[1].get()); |
| 591 | NamespaceInfo ExpectedParentA(EmptySID); |
| 592 | ExpectedParentA.Children.Namespaces.emplace_back(args: EmptySID, args: "A" , |
| 593 | args: InfoType::IT_namespace); |
| 594 | CheckNamespaceInfo(Expected: &ExpectedParentA, Actual: ParentA); |
| 595 | |
| 596 | NamespaceInfo *ParentB = InfoAsNamespace(I: Infos[3].get()); |
| 597 | NamespaceInfo ExpectedParentB(EmptySID); |
| 598 | ExpectedParentB.Children.Namespaces.emplace_back( |
| 599 | args: EmptySID, args: "B" , args: InfoType::IT_namespace, args: "A::B" , args: "A" ); |
| 600 | CheckNamespaceInfo(Expected: &ExpectedParentB, Actual: ParentB); |
| 601 | } |
| 602 | |
| 603 | TEST(SerializeTests, emitTypedefs) { |
| 604 | EmittedInfoList Infos; |
| 605 | ExtractInfosFromCode(Code: "typedef int MyInt; using MyDouble = double;" , NumExpectedInfos: 2, |
| 606 | /*Public=*/false, EmittedInfos&: Infos); |
| 607 | |
| 608 | // First info will be the global namespace with the typedef in it. |
| 609 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
| 610 | ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size()); |
| 611 | |
| 612 | const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0]; |
| 613 | EXPECT_EQ("MyInt" , FirstTD.Name); |
| 614 | EXPECT_FALSE(FirstTD.IsUsing); |
| 615 | EXPECT_EQ("int" , FirstTD.Underlying.Type.Name); |
| 616 | |
| 617 | // The second will be another global namespace with the using in it (the |
| 618 | // global namespace is duplicated because the items haven't been merged at the |
| 619 | // serialization phase of processing). |
| 620 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
| 621 | ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size()); |
| 622 | |
| 623 | // Second is the "using" typedef. |
| 624 | const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0]; |
| 625 | EXPECT_EQ("MyDouble" , SecondTD.Name); |
| 626 | EXPECT_TRUE(SecondTD.IsUsing); |
| 627 | EXPECT_EQ("double" , SecondTD.Underlying.Type.Name); |
| 628 | } |
| 629 | |
| 630 | TEST(SerializeTests, emitFunctionTemplate) { |
| 631 | EmittedInfoList Infos; |
| 632 | // A template and a specialization. |
| 633 | ExtractInfosFromCode(Code: "template<typename T = int> bool GetFoo(T);\n" |
| 634 | "template<> bool GetFoo<bool>(bool);" , |
| 635 | NumExpectedInfos: 2, |
| 636 | /*Public=*/false, EmittedInfos&: Infos); |
| 637 | |
| 638 | // First info will be the global namespace. |
| 639 | NamespaceInfo *GlobalNS1 = InfoAsNamespace(I: Infos[0].get()); |
| 640 | ASSERT_EQ(1u, GlobalNS1->Children.Functions.size()); |
| 641 | |
| 642 | const FunctionInfo &Func1 = GlobalNS1->Children.Functions[0]; |
| 643 | EXPECT_EQ("GetFoo" , Func1.Name); |
| 644 | ASSERT_TRUE(Func1.Template); |
| 645 | EXPECT_FALSE(Func1.Template->Specialization); // Not a specialization. |
| 646 | |
| 647 | // Template parameter. |
| 648 | ASSERT_EQ(1u, Func1.Template->Params.size()); |
| 649 | EXPECT_EQ("typename T = int" , Func1.Template->Params[0].Contents); |
| 650 | |
| 651 | // The second will be another global namespace with the function in it (the |
| 652 | // global namespace is duplicated because the items haven't been merged at the |
| 653 | // serialization phase of processing). |
| 654 | NamespaceInfo *GlobalNS2 = InfoAsNamespace(I: Infos[1].get()); |
| 655 | ASSERT_EQ(1u, GlobalNS2->Children.Functions.size()); |
| 656 | |
| 657 | // This one is a template specialization. |
| 658 | const FunctionInfo &Func2 = GlobalNS2->Children.Functions[0]; |
| 659 | EXPECT_EQ("GetFoo" , Func2.Name); |
| 660 | ASSERT_TRUE(Func2.Template); |
| 661 | EXPECT_TRUE(Func2.Template->Params.empty()); // No template params. |
| 662 | ASSERT_TRUE(Func2.Template->Specialization); |
| 663 | |
| 664 | // Specialization values. |
| 665 | ASSERT_EQ(1u, Func2.Template->Specialization->Params.size()); |
| 666 | EXPECT_EQ("bool" , Func2.Template->Specialization->Params[0].Contents); |
| 667 | EXPECT_EQ(Func1.USR, Func2.Template->Specialization->SpecializationOf); |
| 668 | |
| 669 | EXPECT_EQ("bool" , Func2.ReturnType.Type.Name); |
| 670 | } |
| 671 | |
| 672 | TEST(SerializeTests, emitClassTemplate) { |
| 673 | EmittedInfoList Infos; |
| 674 | // This will generate 2x the number of infos: each Record will be followed by |
| 675 | // a copy of the global namespace containing it (this test checks the data |
| 676 | // pre-merge). |
| 677 | ExtractInfosFromCode( |
| 678 | Code: "template<int I> class MyTemplate { int i[I]; };\n" |
| 679 | "template<> class MyTemplate<0> {};\n" |
| 680 | "template<typename T, int U = 1> class OtherTemplate {};\n" |
| 681 | "template<int U> class OtherTemplate<MyTemplate<0>, U> {};" , |
| 682 | NumExpectedInfos: 8, |
| 683 | /*Public=*/false, EmittedInfos&: Infos); |
| 684 | |
| 685 | // First record. |
| 686 | const RecordInfo *Rec1 = InfoAsRecord(I: Infos[0].get()); |
| 687 | EXPECT_EQ("MyTemplate" , Rec1->Name); |
| 688 | ASSERT_TRUE(Rec1->Template); |
| 689 | EXPECT_FALSE(Rec1->Template->Specialization); // Not a specialization. |
| 690 | |
| 691 | // First record template parameter. |
| 692 | ASSERT_EQ(1u, Rec1->Template->Params.size()); |
| 693 | EXPECT_EQ("int I" , Rec1->Template->Params[0].Contents); |
| 694 | |
| 695 | // Second record. |
| 696 | const RecordInfo *Rec2 = InfoAsRecord(I: Infos[2].get()); |
| 697 | EXPECT_EQ("MyTemplate" , Rec2->Name); |
| 698 | ASSERT_TRUE(Rec2->Template); |
| 699 | EXPECT_TRUE(Rec2->Template->Params.empty()); // No template params. |
| 700 | ASSERT_TRUE(Rec2->Template->Specialization); |
| 701 | |
| 702 | // Second record specialization values. |
| 703 | ASSERT_EQ(1u, Rec2->Template->Specialization->Params.size()); |
| 704 | EXPECT_EQ("0" , Rec2->Template->Specialization->Params[0].Contents); |
| 705 | EXPECT_EQ(Rec1->USR, Rec2->Template->Specialization->SpecializationOf); |
| 706 | |
| 707 | // Third record. |
| 708 | const RecordInfo *Rec3 = InfoAsRecord(I: Infos[4].get()); |
| 709 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
| 710 | ASSERT_TRUE(Rec3->Template); |
| 711 | |
| 712 | // Third record template parameters. |
| 713 | ASSERT_EQ(2u, Rec3->Template->Params.size()); |
| 714 | EXPECT_EQ("typename T" , Rec3->Template->Params[0].Contents); |
| 715 | EXPECT_EQ("int U = 1" , Rec3->Template->Params[1].Contents); |
| 716 | |
| 717 | // Fourth record. |
| 718 | const RecordInfo *Rec4 = InfoAsRecord(I: Infos[6].get()); |
| 719 | EXPECT_EQ("OtherTemplate" , Rec3->Name); |
| 720 | ASSERT_TRUE(Rec4->Template); |
| 721 | ASSERT_TRUE(Rec4->Template->Specialization); |
| 722 | |
| 723 | // Fourth record template + specialization parameters. |
| 724 | ASSERT_EQ(1u, Rec4->Template->Params.size()); |
| 725 | EXPECT_EQ("int U" , Rec4->Template->Params[0].Contents); |
| 726 | ASSERT_EQ(2u, Rec4->Template->Specialization->Params.size()); |
| 727 | EXPECT_EQ("MyTemplate<0>" , |
| 728 | Rec4->Template->Specialization->Params[0].Contents); |
| 729 | EXPECT_EQ("U" , Rec4->Template->Specialization->Params[1].Contents); |
| 730 | } |
| 731 | |
| 732 | } // namespace doc |
| 733 | } // end namespace clang |
| 734 | |