File indexing completed on 2024-05-19 05:42:05

0001 // ct_lvtclp_testudt.t.cpp                                           -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 // This file is home to tests for adding user defined types to the database
0021 
0022 #include <ct_lvtmdb_fileobject.h>
0023 #include <ct_lvtmdb_functionobject.h>
0024 #include <ct_lvtmdb_methodobject.h>
0025 #include <ct_lvtmdb_namespaceobject.h>
0026 #include <ct_lvtmdb_objectstore.h>
0027 #include <ct_lvtmdb_typeobject.h>
0028 
0029 #include <ct_lvtclp_testutil.h>
0030 #include <ct_lvtshr_graphenums.h>
0031 
0032 #include <algorithm>
0033 #include <initializer_list>
0034 #include <string>
0035 #include <tuple>
0036 #include <unordered_set>
0037 #include <vector>
0038 
0039 #include <catch2-local-includes.h>
0040 
0041 #include <clang/Basic/Version.h>
0042 
0043 using namespace Codethink::lvtclp;
0044 using namespace Codethink::lvtmdb;
0045 using namespace Codethink;
0046 
0047 using UDTKind = Codethink::lvtshr::UDTKind;
0048 
0049 const PyDefaultGilReleasedContext defaultGilContextForTesting;
0050 
0051 TEST_CASE("User defined type")
0052 {
0053     const static char *source = "namespace foo { class C {}; class D; }";
0054     ObjectStore session;
0055     REQUIRE(Test_Util::runOnCode(session, source, "testUserDefinedType.cpp"));
0056 
0057     TypeObject *C = nullptr;
0058     TypeObject *D = nullptr;
0059     NamespaceObject *foo = nullptr;
0060     FileObject *file = nullptr;
0061 
0062     session.withROLock([&] {
0063         C = session.getType("foo::C");
0064         D = session.getType("foo::D");
0065         foo = session.getNamespace("foo");
0066         file = session.getFile("testUserDefinedType.cpp");
0067     });
0068 
0069     REQUIRE(C);
0070     REQUIRE(D);
0071     REQUIRE(foo);
0072     REQUIRE(file);
0073 
0074     C->withROLock([&] {
0075         REQUIRE(C->name() == "C");
0076         REQUIRE(C->kind() == UDTKind::Class);
0077         REQUIRE(C->access() == lvtshr::AccessSpecifier::e_none);
0078         REQUIRE(C->parentNamespace() == foo);
0079         REQUIRE(!C->parent());
0080     });
0081 
0082     D->withROLock([&] {
0083         REQUIRE(D->name() == "D");
0084         REQUIRE(D->kind() == UDTKind::Class);
0085         REQUIRE(D->access() == lvtshr::AccessSpecifier::e_none);
0086         REQUIRE(D->parentNamespace() == foo);
0087         REQUIRE(!D->parent());
0088     });
0089 
0090     foo->withROLock([&] {
0091         const auto classChildren = foo->typeChildren();
0092         auto childIt = std::find(classChildren.begin(), classChildren.end(), C);
0093         REQUIRE(childIt != classChildren.end());
0094         childIt = std::find(classChildren.begin(), classChildren.end(), D);
0095         REQUIRE(childIt != classChildren.end());
0096     });
0097 
0098     file->withROLock([&] {
0099         const auto classes = file->types();
0100         auto classes_it = std::find(classes.begin(), classes.end(), C);
0101         REQUIRE(classes_it != classes.end());
0102         classes_it = std::find(classes.begin(), classes.end(), D);
0103         REQUIRE(classes_it != classes.end());
0104     });
0105     C->withROLock([&] {
0106         auto files = C->files();
0107         auto files_it = std::find(files.begin(), files.end(), file);
0108         REQUIRE(files_it != files.end());
0109     });
0110 
0111     D->withROLock([&] {
0112         auto files = D->files();
0113         auto files_it = std::find(files.begin(), files.end(), file);
0114         REQUIRE(files_it != files.end());
0115     });
0116 }
0117 
0118 TEST_CASE("Struct declaration")
0119 {
0120     const static char *source = "struct S {};";
0121     ObjectStore session;
0122     REQUIRE(Test_Util::runOnCode(session, source, "testStructDeclaration.cpp"));
0123 
0124     // assume things we tested in testUserDefinedType are still good here
0125     TypeObject *S = nullptr;
0126     session.withROLock([&] {
0127         S = session.getType("S");
0128     });
0129     REQUIRE(S);
0130 
0131     S->withROLock([&] {
0132         REQUIRE(S->kind() == UDTKind::Struct);
0133     });
0134 }
0135 
0136 TEST_CASE("Union declaration")
0137 {
0138     const static char *source = "union U {};";
0139     ObjectStore session;
0140     REQUIRE(Test_Util::runOnCode(session, source, "testUnionDeclaration.cpp"));
0141 
0142     TypeObject *U = nullptr;
0143     session.withROLock([&] {
0144         U = session.getType("U");
0145     });
0146     // assume things we tested in testUserDefinedType are still good here
0147     REQUIRE(U);
0148 
0149     U->withROLock([&] {
0150         REQUIRE(U->kind() == UDTKind::Union);
0151     });
0152 }
0153 
0154 TEST_CASE("Local class")
0155 {
0156     const static char *source =
0157         R"(void fn()
0158    {
0159        class C {};
0160    }
0161 )";
0162     ObjectStore session;
0163     REQUIRE(Test_Util::runOnCode(session, source, "testLocalClass.cpp"));
0164 
0165     session.withROLock([&] {
0166         REQUIRE(session.types().empty());
0167     });
0168 
0169     // we skip local classes
0170 }
0171 
0172 TEST_CASE("Anon class")
0173 {
0174     const static char *source = "class {} foo;";
0175     ObjectStore session;
0176     REQUIRE(Test_Util::runOnCode(session, source, "testAnonClass.cpp"));
0177     session.withROLock([&] {
0178         REQUIRE(session.types().empty());
0179     });
0180 
0181     // we skip anon classes
0182 }
0183 
0184 TEST_CASE("Nested classes")
0185 {
0186     const static char *source = R"(
0187 namespace foo {
0188 
0189 class C {
0190   public:
0191     class Pub {};
0192 
0193   protected:
0194     class Prot {};
0195 
0196   private:
0197     class Priv {};
0198 };
0199 
0200 }
0201 )";
0202     ObjectStore session;
0203     REQUIRE(Test_Util::runOnCode(session, source, "testtNestedClasses.cpp"));
0204 
0205     TypeObject *C = nullptr;
0206     TypeObject *Pub = nullptr;
0207     TypeObject *Prot = nullptr;
0208     TypeObject *Priv = nullptr;
0209     NamespaceObject *foo = nullptr;
0210 
0211     session.withROLock([&] {
0212         C = session.getType("foo::C");
0213         Pub = session.getType("foo::C::Pub");
0214         Prot = session.getType("foo::C::Prot");
0215         Priv = session.getType("foo::C::Priv");
0216         foo = session.getNamespace("foo");
0217     });
0218 
0219     REQUIRE(Priv);
0220     REQUIRE(Prot);
0221     REQUIRE(Pub);
0222     REQUIRE(C);
0223     REQUIRE(foo);
0224 
0225     for (const auto& [klass, access] :
0226          std::map<TypeObject *, lvtshr::AccessSpecifier>{{Pub, lvtshr::AccessSpecifier::e_public},
0227                                                          {Prot, lvtshr::AccessSpecifier::e_protected},
0228                                                          {Priv, lvtshr::AccessSpecifier::e_private}}) {
0229         klass->withROLock([&, klass = klass, access = access] {
0230             REQUIRE(klass->parentNamespace() == foo);
0231             REQUIRE(klass->parent() == C);
0232             REQUIRE(klass->access() == access);
0233         });
0234     }
0235 }
0236 
0237 TEST_CASE("Is-A")
0238 {
0239     const static char *source =
0240         R"(class C {};
0241    class D : public C {};
0242 )";
0243 
0244     ObjectStore session;
0245     REQUIRE(Test_Util::runOnCode(session, source, "testIsA.cpp"));
0246     REQUIRE(Test_Util::isAExists("D", "C", session));
0247 }
0248 
0249 TEST_CASE("Virtual Is-A")
0250 {
0251     const static char *source =
0252         R"(class C {};
0253    class D: public virtual C {};
0254 )";
0255 
0256     ObjectStore session;
0257     REQUIRE(Test_Util::runOnCode(session, source, "testVirtualIsA.cpp"));
0258     REQUIRE(Test_Util::isAExists("D", "C", session));
0259 }
0260 
0261 TEST_CASE("Template Is-A")
0262 {
0263     const static char *source =
0264         R"(class C {};
0265 
0266    template <typename T>
0267    class D {
0268        T t;
0269    };
0270 
0271    class E: public D<C> {
0272    };
0273 )";
0274 
0275     ObjectStore session;
0276     REQUIRE(Test_Util::runOnCode(session, source, "testTemplateIsA.cpp"));
0277 
0278     REQUIRE(Test_Util::isAExists("E", "D", session));
0279     REQUIRE(Test_Util::usesInTheInterfaceExists("E", "C", session));
0280     REQUIRE(!Test_Util::usesInTheImplementationExists("D", "T", session));
0281 }
0282 
0283 TEST_CASE("Template virtual Is-A")
0284 {
0285     const static char *source =
0286         R"(class C {};
0287 
0288    template <typename T>
0289    class D {
0290        T t;
0291    };
0292 
0293    class E: public virtual D<C> {
0294    };
0295 )";
0296 
0297     ObjectStore session;
0298     REQUIRE(Test_Util::runOnCode(session, source, "testTemplateVirtualIsA.cpp"));
0299 
0300     REQUIRE(Test_Util::isAExists("E", "D", session));
0301     REQUIRE(Test_Util::usesInTheInterfaceExists("E", "C", session));
0302     REQUIRE(!Test_Util::usesInTheImplementationExists("D", "T", session));
0303 }
0304 
0305 TEST_CASE("Uses in method arguments")
0306 {
0307     static const char *source = R"(
0308 class C {};
0309 class D {
0310     void method(const C& c)
0311     {
0312     }
0313 };
0314 )";
0315     ObjectStore session;
0316     REQUIRE(Test_Util::runOnCode(session, source, "testUsesInMethodArguments.cpp"));
0317 
0318     TypeObject *C = nullptr;
0319     TypeObject *D = nullptr;
0320     MethodObject *method = nullptr;
0321 
0322     session.withROLock([&] {
0323         C = session.getType("C");
0324         D = session.getType("D");
0325 
0326         method = session.getMethod("D::method", "method(const C & c)", std::string{}, "void");
0327     });
0328 
0329     REQUIRE(D);
0330     REQUIRE(C);
0331     REQUIRE(method);
0332 
0333     C->withROLock([&] {
0334         REQUIRE(C->methods().empty());
0335 
0336         // TODO: Make sure this is needed - We are not using this anywhere.
0337         /*  Not implemented in LVTMDB
0338                 const auto usesInMethodArgs = C->usesInMethodArguments();
0339                 REQUIRE(usesInMethodArgs.size() == 1);
0340                 it = std::find(usesInMethodArgs.begin(), usesInMethodArgs.end(), method);
0341                 REQUIRE(it != methods.end());
0342         */
0343     });
0344 
0345     D->withROLock([&] {
0346         const auto methods = D->methods();
0347         REQUIRE(methods.size() == 1);
0348         auto it = std::find(methods.begin(), methods.end(), method);
0349         REQUIRE(it != methods.end());
0350 
0351         // TODO: Make sure this is needed.
0352         // Not Implemented in LVTMDB
0353         // REQUIRE(D->usesInMethodArguments().empty());
0354     });
0355 }
0356 
0357 TEST_CASE("Uses in field type")
0358 {
0359     static const char *source = R"(
0360 class C {};
0361 class D {
0362     C field;
0363 };
0364 )";
0365     ObjectStore session;
0366     REQUIRE(Test_Util::runOnCode(session, source, "testUsesInFieldType.cpp"));
0367     TypeObject *C = nullptr;
0368     TypeObject *D = nullptr;
0369     FieldObject *field = nullptr;
0370 
0371     session.withROLock([&] {
0372         C = session.getType("C");
0373         D = session.getType("D");
0374         field = session.getField("D::field");
0375     });
0376 
0377     REQUIRE(C);
0378     REQUIRE(D);
0379     REQUIRE(field);
0380 
0381     D->withRWLock([&] {
0382         const auto fields = D->fields();
0383         REQUIRE(fields.size() == 1);
0384         auto it = std::find(fields.begin(), fields.end(), field);
0385         REQUIRE(it != fields.end());
0386         // REQUIRE(D->usesInFieldType().empty());
0387     });
0388 
0389     C->withROLock([&] {
0390     // Not implemented yet on lvtmdb.
0391 #if 0
0392         const auto usesInFieldTypes = C->usesInFieldType();
0393         REQUIRE(usesInFieldTypes.size() == 1);
0394         it = std::find(usesInFieldTypes.begin(), usesInFieldTypes.end(), field);
0395         REQUIRE(it != usesInFieldTypes.end());
0396 #endif
0397         REQUIRE(C->fields().empty());
0398     });
0399 }
0400 
0401 TEST_CASE("Uses in field array type")
0402 {
0403     static const char *source = R"(
0404 class C {};
0405 class D {
0406     C field[3];
0407 };
0408 )";
0409     ObjectStore session;
0410     REQUIRE(Test_Util::runOnCode(session, source, "testUsesInFieldType.cpp"));
0411 
0412     TypeObject *C = nullptr;
0413     TypeObject *D = nullptr;
0414     FieldObject *field = nullptr;
0415 
0416     session.withROLock([&] {
0417         C = session.getType("C");
0418         D = session.getType("D");
0419         field = session.getField("D::field");
0420     });
0421 
0422     REQUIRE(C);
0423     REQUIRE(D);
0424     REQUIRE(field);
0425 
0426     D->withRWLock([&] {
0427         const auto fields = D->fields();
0428         REQUIRE(fields.size() == 1);
0429         auto it = std::find(fields.begin(), fields.end(), field);
0430         REQUIRE(it != fields.end());
0431         // REQUIRE(D->usesInFieldType().empty());
0432     });
0433 
0434     C->withROLock([&] {
0435     // Not implemented yet on lvtmdb.
0436 #if 0
0437         const auto usesInFieldTypes = C->usesInFieldType();
0438         REQUIRE(usesInFieldTypes.size() == 1);
0439         it = std::find(usesInFieldTypes.begin(), usesInFieldTypes.end(), field);
0440         REQUIRE(it != usesInFieldTypes.end());
0441 #endif
0442         REQUIRE(C->fields().empty());
0443     });
0444 
0445     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0446 }
0447 
0448 TEST_CASE("Class template")
0449 {
0450     static const char *source = R"(
0451 template <typename T>
0452 class C {
0453 };
0454 
0455 C<int> c;
0456 )";
0457     ObjectStore session;
0458     REQUIRE(Test_Util::runOnCode(session, source, "testClassTemplate.cpp"));
0459 
0460     session.withROLock([&] {
0461         REQUIRE(session.getType("C"));
0462         REQUIRE(session.types().size() == 1);
0463     });
0464 
0465     // check we didn't separately add C<int>
0466 }
0467 
0468 TEST_CASE("Class non type template")
0469 {
0470     static const char *source = R"(
0471 template <unsigned N>
0472 class CharArray {
0473     char arr[N];
0474 };
0475 
0476 CharArray<1> arr;
0477 )";
0478     ObjectStore session;
0479     REQUIRE(Test_Util::runOnCode(session, source, "testClassNonTypeTemplate.cpp"));
0480     session.withROLock([&] {
0481         TypeObject *CharArray = session.getType("CharArray");
0482         REQUIRE(CharArray);
0483 
0484         // check we didn't separately add CharArray<1>
0485         REQUIRE(session.types().size() == 1);
0486     });
0487 }
0488 
0489 TEST_CASE("Class template template")
0490 {
0491     static const char *source = R"(
0492 template <class T>
0493 class C {};
0494 
0495 template <template<class> class T>
0496 class ApplyChar {
0497     T<char> t;
0498 };
0499 
0500 ApplyChar<C> a;
0501 )";
0502     ObjectStore session;
0503     REQUIRE(Test_Util::runOnCode(session, source, "testClassTemplateTemplate.cpp"));
0504     session.withROLock([&] {
0505         REQUIRE(session.getType("C"));
0506         REQUIRE(session.getType("ApplyChar"));
0507         REQUIRE(session.types().size() == 2);
0508     });
0509 
0510     // check we didn't separately add ApplyChar<C>
0511 }
0512 
0513 TEST_CASE("Type aliase namespace")
0514 {
0515     static const char *source = R"(
0516 namespace std {
0517 class Something;
0518 }
0519 
0520 namespace foo {
0521 using Something = std::Something;
0522 }
0523 
0524 class C {
0525   public:
0526     foo::Something& some;
0527 };
0528 )";
0529     ObjectStore session;
0530     REQUIRE(Test_Util::runOnCode(session, source, "testTypeAliasNamespace.cpp"));
0531 
0532     NamespaceObject *foo = nullptr;
0533     TypeObject *fooSomething = nullptr;
0534     FileObject *file = nullptr;
0535 
0536     session.withROLock([&] {
0537         foo = session.getNamespace("foo");
0538         fooSomething = session.getType("foo::Something");
0539         file = session.getFile("testTypeAliasNamespace.cpp");
0540     });
0541 
0542     REQUIRE(fooSomething);
0543     REQUIRE(foo);
0544     REQUIRE(file);
0545 
0546     fooSomething->withROLock([&] {
0547         REQUIRE(fooSomething->name() == "Something");
0548         REQUIRE(fooSomething->kind() == UDTKind::TypeAlias);
0549         REQUIRE(fooSomething->access() == lvtshr::AccessSpecifier::e_none);
0550         REQUIRE(fooSomething->parentNamespace() == foo);
0551         REQUIRE(!fooSomething->parent());
0552     });
0553 
0554     file->withROLock([&] {
0555         const auto udts = file->types();
0556         const auto udts_it = std::find(udts.begin(), udts.end(), fooSomething);
0557         REQUIRE(udts_it != udts.end());
0558     });
0559 
0560     REQUIRE(Test_Util::usesInTheImplementationExists("foo::Something", "std::Something", session));
0561     REQUIRE(Test_Util::usesInTheInterfaceExists("C", "foo::Something", session));
0562 }
0563 
0564 TEST_CASE("enum")
0565 {
0566     static const char *source = R"(
0567 namespace foo {
0568 struct Util {
0569     enum Enum {
0570         e_ONE = 1,
0571         e_TWO,
0572     };
0573 };
0574 
0575 enum class Byte : unsigned char {};
0576 }
0577 )";
0578     ObjectStore session;
0579     REQUIRE(Test_Util::runOnCode(session, source, "testEnum.cpp"));
0580 
0581     TypeObject *unscoped = nullptr;
0582     TypeObject *scoped = nullptr;
0583     TypeObject *util = nullptr;
0584     FileObject *file = nullptr;
0585     NamespaceObject *foo = nullptr;
0586 
0587     session.withROLock([&] {
0588         unscoped = session.getType("foo::Util::Enum");
0589         scoped = session.getType("foo::Byte");
0590         util = session.getType("foo::Util");
0591         file = session.getFile("testEnum.cpp");
0592         foo = session.getNamespace("foo");
0593     });
0594 
0595     REQUIRE(unscoped);
0596     REQUIRE(scoped);
0597     REQUIRE(util);
0598     REQUIRE(file);
0599     REQUIRE(foo);
0600 
0601     unscoped->withROLock([&] {
0602         REQUIRE(unscoped->name() == "Enum");
0603     });
0604 
0605     scoped->withROLock([&] {
0606         REQUIRE(scoped->name() == "Byte");
0607     });
0608 
0609     std::vector<TypeObject *> udts;
0610     file->withROLock([&] {
0611         udts = file->types();
0612     });
0613 
0614     for (const auto& e : {unscoped, scoped}) {
0615         e->withROLock([&] {
0616             REQUIRE(e->kind() == UDTKind::Enum);
0617             const auto udts_it = std::find(udts.begin(), udts.end(), e);
0618             REQUIRE(udts_it != udts.end());
0619             REQUIRE(e->parentNamespace() == foo);
0620         });
0621     }
0622 
0623     unscoped->withROLock([&] {
0624         REQUIRE(unscoped->parent() == util);
0625         REQUIRE(unscoped->access() == lvtshr::AccessSpecifier::e_public);
0626     });
0627 
0628     scoped->withROLock([&] {
0629         REQUIRE(!scoped->parent());
0630         REQUIRE(scoped->access() == lvtshr::AccessSpecifier::e_none);
0631     });
0632 }
0633 
0634 TEST_CASE("Internal linkage")
0635 {
0636     static const char *source = R"(
0637 namespace {
0638 class C {};
0639 typedef C D;
0640 
0641 namespace inner {
0642     class Nested1 {
0643         class NestedSquared {};
0644 
0645         typedef class NestedSquared NS;
0646     };
0647 
0648     typedef Nested1 N;
0649 }
0650 }
0651 
0652 namespace outer {
0653     namespace {
0654         class Nested2 {
0655             class NestedSquared2 {};
0656             typedef NestedSquared2 NS2;
0657         };
0658 
0659         typedef Nested2 N2;
0660     }
0661 })";
0662     ObjectStore session;
0663     REQUIRE(Test_Util::runOnCode(session, source, "testInternalLinkage.cpp"));
0664 
0665     session.withROLock([&] {
0666         REQUIRE(session.types().empty());
0667     });
0668 }
0669 
0670 TEST_CASE("Weird templates")
0671 {
0672     // mostly based on bdlat_typetraits.h
0673     static const char *source = R"(
0674 // baljsn_encodingstyle.h
0675 namespace baljsn {
0676 class EncodingStyle {
0677   public:
0678     enum Value {
0679       e_COMPACT = 0,
0680       e_PRETTY = 1
0681     };
0682 
0683     static int fromInt(Value *result, int number);
0684 };
0685 }
0686 
0687 template <class TYPE>
0688 struct bdlat_BasicEnumerationWrapper;
0689 
0690 #define BDLAT_DECL_ENUMERATION_TRAITS(ClassName)                              \
0691     template <>                                                               \
0692     struct bdlat_BasicEnumerationWrapper<ClassName::Value> : ClassName {      \
0693         typedef ClassName Wrapper;                                            \
0694     };
0695 
0696 BDLAT_DECL_ENUMERATION_TRAITS(baljsn::EncodingStyle)
0697 
0698 template <class TYPE>
0699 int bdlat_enumFromInt(TYPE *result, int number)
0700 {
0701     typedef typename bdlat_BasicEnumerationWrapper<TYPE>::Wrapper Wrapper;
0702     return Wrapper::fromInt(result, number);
0703 }
0704 
0705 int main()
0706 {
0707     baljsn::EncodingStyle::Value style;
0708     return bdlat_enumFromInt(&style, 1); // force template instantiation
0709 }
0710 )";
0711     ObjectStore session;
0712     REQUIRE(Test_Util::runOnCode(session, source, "testWeirdTemplates.cpp"));
0713 
0714     session.withROLock([&] {
0715         auto *bad = session.getType("bdlat_BasicEnumerationWrapper<baljsn::EncodingStyle::Value>::Wrapper");
0716         REQUIRE(!bad);
0717     });
0718 }
0719 
0720 TEST_CASE("C functions")
0721 {
0722     const static char *source =
0723         "typedef struct { int x; } Foo;"
0724         "static void g() {}"
0725         "int h(const Foo*);"
0726         "int f(Foo *foo) { g(); return h(foo); }"
0727         "int h(const Foo*) { return 42; }";
0728     ObjectStore session;
0729     REQUIRE(Test_Util::runOnCode(session, source, "testFreeFunction.cpp"));
0730     auto obtainedFunctionNames = [&]() {
0731         auto res = std::unordered_set<std::string>{};
0732         session.withROLock([&]() {
0733             for (auto const& [f, fd] : session.functions()) {
0734                 auto& _fd = fd;
0735                 fd->withROLock([&]() {
0736                     res.insert(_fd->qualifiedName());
0737                 });
0738             }
0739         });
0740         return res;
0741     }();
0742     {
0743         using Expected = decltype(obtainedFunctionNames);
0744         REQUIRE(obtainedFunctionNames == Expected{"f", "g", "h"});
0745     }
0746 
0747     auto obtainedFunctionsInFiles = [&]() {
0748         auto res = std::unordered_set<std::string>{};
0749         auto *file = [&]() {
0750             auto *ret = (FileObject *) nullptr;
0751             session.withROLock([&]() {
0752                 ret = session.getFile("testFreeFunction.cpp");
0753             });
0754             return ret;
0755         }();
0756         file->withROLock([&]() {
0757             for (auto const& func : file->globalFunctions()) {
0758                 func->withROLock([&]() {
0759                     res.insert(func->qualifiedName());
0760                 });
0761             }
0762         });
0763         return res;
0764     }();
0765     {
0766         using Expected = decltype(obtainedFunctionsInFiles);
0767         REQUIRE(obtainedFunctionsInFiles == Expected{"f", "g", "h"});
0768     }
0769 }