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

0001 // ct_lvtclp_testvar.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 for tests for CodebaseDbVisitor::VisitVarDecl
0021 // (except for static class member variables, which are in testfield)
0022 
0023 #include <ct_lvtmdb_namespaceobject.h>
0024 #include <ct_lvtmdb_objectstore.h>
0025 #include <ct_lvtmdb_typeobject.h>
0026 #include <ct_lvtmdb_variableobject.h>
0027 
0028 #include <ct_lvtclp_testutil.h>
0029 
0030 #include <catch2-local-includes.h>
0031 #include <clang/Basic/Version.h>
0032 
0033 using namespace Codethink::lvtclp;
0034 using namespace Codethink::lvtmdb;
0035 
0036 const PyDefaultGilReleasedContext defaultGilContextForTesting;
0037 
0038 TEST_CASE("Local variable declaration")
0039 {
0040     const static char *source = R"(
0041 class C {};
0042 
0043 class D {
0044     void method()
0045     {
0046         C c;
0047     }
0048 };
0049 )";
0050     ObjectStore session;
0051     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarDecl.cpp"));
0052 
0053     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0054 }
0055 
0056 TEST_CASE("Local variable anon parent")
0057 {
0058     const static char *source = R"(
0059 class C {};
0060 
0061 class {
0062     void method()
0063     {
0064         C c;
0065     }
0066 } d;
0067 )";
0068     ObjectStore session;
0069     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarAnonParent.cpp"));
0070 
0071     // check there are no usesInTheImpl relationships in the database
0072     session.withROLock([&] {
0073         for (const auto& [_, klass] : session.types()) {
0074             auto *k = klass.get();
0075             klass->withROLock([k] {
0076                 REQUIRE(k->usesInTheImplementation().empty());
0077             });
0078         }
0079     });
0080 }
0081 
0082 TEST_CASE("Local variable anon type")
0083 {
0084     const static char *source = R"(
0085 class C {
0086     void method()
0087     {
0088         class {} c;
0089     }
0090 };
0091 )";
0092     ObjectStore session;
0093     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarAnonType.cpp"));
0094 
0095     // check there are no usesInTheImpl relationships in the database
0096     session.withROLock([&] {
0097         for (const auto& [_, klass] : session.types()) {
0098             auto *k = klass.get();
0099             klass->withROLock([k] {
0100                 REQUIRE(k->usesInTheImplementation().empty());
0101             });
0102         }
0103     });
0104 }
0105 
0106 TEST_CASE("Local variable template method")
0107 {
0108     const static char *source = R"(
0109 class T {};
0110 
0111 class C {};
0112 
0113 class D {};
0114 
0115 class E {
0116   public:
0117     template <typename T>
0118     void method()
0119     {
0120         C c;
0121     }
0122 };
0123 
0124 void function()
0125 {
0126     E e;
0127     e.method<D>();
0128 }
0129 )";
0130     ObjectStore session;
0131     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarTemplateMethod.cpp"));
0132 
0133     REQUIRE(Test_Util::usesInTheImplementationExists("E", "C", session));
0134     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "T", session));
0135     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "D", session));
0136 }
0137 
0138 TEST_CASE("Local variable template method 2")
0139 {
0140     const static char *source = R"(
0141 class T {};
0142 
0143 class D {};
0144 
0145 class E {
0146   public:
0147     template <typename T>
0148     void method()
0149     {
0150         T t;
0151     }
0152 };
0153 
0154 void function()
0155 {
0156     E e;
0157     e.method<D>();
0158 }
0159 )";
0160     ObjectStore session;
0161     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarTemplateMethod2.cpp"));
0162 
0163     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "T", session));
0164     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "D", session));
0165 }
0166 
0167 TEST_CASE("Local variable template class")
0168 {
0169     const static char *source = R"(
0170 class T {};
0171 class C {};
0172 class D {};
0173 
0174 template <typename T>
0175 class E {
0176   public:
0177     void method()
0178     {
0179         C c;
0180     }
0181 };
0182 
0183 void function()
0184 {
0185     E<D> ed;
0186     ed.method();
0187 }
0188 )";
0189     ObjectStore session;
0190     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarTemplateClass.cpp"));
0191 
0192     REQUIRE(Test_Util::usesInTheImplementationExists("E", "C", session));
0193     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "T", session));
0194     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "D", session));
0195 }
0196 
0197 TEST_CASE("Local variable template class 2")
0198 {
0199     const static char *source = R"(
0200 class T {};
0201 class D {};
0202 
0203 template <typename T>
0204 class E {
0205   public:
0206     void method()
0207     {
0208         T t;
0209     }
0210 };
0211 
0212 void function()
0213 {
0214     E<D> ed;
0215     ed.method();
0216 }
0217 )";
0218     ObjectStore session;
0219     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarTemplateClass2.cpp"));
0220 
0221     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "T", session));
0222     REQUIRE(!Test_Util::usesInTheImplementationExists("E", "D", session));
0223 }
0224 
0225 TEST_CASE("Local variable lambda in method")
0226 {
0227     const static char *source = R"(
0228 class C {};
0229 
0230 class D {
0231     void method()
0232     {
0233         auto lambda = []() {
0234             C c;
0235         };
0236     }
0237 };
0238 )";
0239     ObjectStore session;
0240     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarLambdaInMethod.cpp"));
0241 
0242     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0243 }
0244 
0245 TEST_CASE("Local variable lambda in lambda in method")
0246 {
0247     const static char *source = R"(
0248 class C {};
0249 
0250 class D {
0251     void method()
0252     {
0253         auto lambda = []() {
0254             auto lambda2 = []() {
0255                 C c;
0256             };
0257         };
0258     }
0259 };
0260 )";
0261     ObjectStore session;
0262     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarLambdaInLambdaInMethod.cpp"));
0263 
0264     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0265 }
0266 
0267 TEST_CASE("Local variable lambda in field")
0268 {
0269     const static char *source = R"(
0270 class C {};
0271 
0272 class D {
0273     // auto not allowed in non-static class member
0274     // auto variables must be initialized at declaration
0275     // only constexpr static members can be initialized at declaration
0276     static constexpr auto lambda = []() {
0277         C c;
0278     };
0279 };
0280 )";
0281     ObjectStore session;
0282     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarLambdaInField.cpp"));
0283 
0284     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0285 }
0286 
0287 TEST_CASE("Local variable in block")
0288 {
0289     const static char *source = R"(
0290 class C {};
0291 
0292 class D {
0293     void method()
0294     {
0295         {
0296             C c;
0297         }
0298     }
0299 };
0300 )";
0301     ObjectStore session;
0302     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarInBlock.cpp"));
0303 
0304     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0305 }
0306 
0307 TEST_CASE("Method arg relation")
0308 {
0309     const static char *source = R"(
0310 class Pub {};
0311 class Prot {};
0312 class Priv {};
0313 
0314 class C {
0315   public:
0316     void pubMethod(Pub p) {}
0317 
0318   protected:
0319     void protMethod(Prot p) {}
0320 
0321   private:
0322     void privMethod(Priv p) {}
0323 };
0324 )";
0325     ObjectStore session;
0326     REQUIRE(Test_Util::runOnCode(session, source, "testMethodArgRelation.cpp"));
0327 
0328     REQUIRE(Test_Util::usesInTheInterfaceExists("C", "Pub", session));
0329     REQUIRE(!Test_Util::usesInTheImplementationExists("C", "Pub", session));
0330     REQUIRE(Test_Util::usesInTheInterfaceExists("C", "Prot", session));
0331     REQUIRE(!Test_Util::usesInTheImplementationExists("C", "Prot", session));
0332     REQUIRE(!Test_Util::usesInTheInterfaceExists("C", "Priv", session));
0333     REQUIRE(Test_Util::usesInTheImplementationExists("C", "Priv", session));
0334 }
0335 
0336 TEST_CASE("Method template arg")
0337 {
0338     const static char *source = R"(
0339 template <typename T>
0340 class C {};
0341 
0342 template <typename T>
0343 class D {};
0344 
0345 class E {};
0346 
0347 class F {};
0348 
0349 // see if we can trip up our parsing
0350 class T {};
0351 
0352 class G {
0353   public:
0354     void method(C<D<E>> cde) {}
0355 
0356     template <typename T>
0357     void templateMethod(C<T> ct) {}
0358 
0359   private:
0360     void privMethod(C<F> cf) {}
0361 };
0362 )";
0363     ObjectStore session;
0364     REQUIRE(Test_Util::runOnCode(session, source, "testMethodTemplateArg.cpp"));
0365 
0366     REQUIRE(Test_Util::usesInTheInterfaceExists("G", "C", session));
0367     REQUIRE(Test_Util::usesInTheInterfaceExists("G", "D", session));
0368     REQUIRE(Test_Util::usesInTheInterfaceExists("G", "E", session));
0369     REQUIRE(!Test_Util::usesInTheInterfaceExists("G", "T", session));
0370     REQUIRE(!Test_Util::usesInTheInterfaceExists("G", "F", session));
0371     REQUIRE(Test_Util::usesInTheImplementationExists("G", "F", session));
0372 }
0373 
0374 TEST_CASE("Lambda args in method")
0375 {
0376     const static char *source = R"(
0377 class C {};
0378 
0379 class D {
0380   public:
0381     void method()
0382     {
0383         auto foo = [](C& c){};
0384     }
0385 };
0386 )";
0387     ObjectStore session;
0388     REQUIRE(Test_Util::runOnCode(session, source, "testLambdaArgsInMethod.cpp"));
0389 
0390     // the method is public but the lambda is an implementation detail inside
0391     // the method so C should be usesInTheImplementation, despite C being used
0392     // as a method parameter
0393     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0394     REQUIRE(!Test_Util::usesInTheInterfaceExists("D", "C", session));
0395 }
0396 
0397 TEST_CASE("Lambda args in field")
0398 {
0399     const static char *source = R"(
0400 class Pub {};
0401 class Prot {};
0402 class Priv {};
0403 
0404 class C {
0405   public:
0406     static constexpr auto pub = [](Pub p) {};
0407 
0408   protected:
0409     static constexpr auto prot = [](Prot& p) {};
0410 
0411   private:
0412     static constexpr auto priv = [](Priv *p) {};
0413 };
0414 )";
0415     ObjectStore session;
0416     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarLambdaInField.cpp"));
0417 
0418     REQUIRE(Test_Util::usesInTheInterfaceExists("C", "Pub", session));
0419     REQUIRE(!Test_Util::usesInTheImplementationExists("C", "Pub", session));
0420     REQUIRE(Test_Util::usesInTheInterfaceExists("C", "Prot", session));
0421     REQUIRE(!Test_Util::usesInTheImplementationExists("C", "Prot", session));
0422     REQUIRE(!Test_Util::usesInTheInterfaceExists("C", "Priv", session));
0423     REQUIRE(Test_Util::usesInTheImplementationExists("C", "Priv", session));
0424 }
0425 
0426 TEST_CASE("Lambda args in lambda in field")
0427 {
0428     const static char *source = R"(
0429 class C {};
0430 
0431 class D {
0432   public:
0433     static constexpr auto pub = []() {
0434             auto innerLambda = [](C c) {};
0435         };
0436 };
0437 )";
0438     ObjectStore session;
0439     REQUIRE(Test_Util::runOnCode(session, source, "testLocalVarLambdaInLambdaInField.cpp"));
0440 
0441     REQUIRE(!Test_Util::usesInTheInterfaceExists("D", "C", session));
0442     REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session));
0443 }
0444 
0445 TEST_CASE("Generic lambda args")
0446 {
0447     const static char *source = R"(
0448 class C {};
0449 
0450 class D {
0451   public:
0452     static constexpr auto lambda = [](auto foo) {};
0453 };
0454 
0455 class E {
0456     void method()
0457     {
0458         D::lambda(C());
0459     }
0460 };
0461 )";
0462     ObjectStore session;
0463     REQUIRE(Test_Util::runOnCode(session, source, "testGenericLambdaArgs.cpp"));
0464 
0465     REQUIRE(Test_Util::usesInTheImplementationExists("E", "D", session));
0466     REQUIRE(Test_Util::usesInTheImplementationExists("E", "C", session));
0467     REQUIRE(!Test_Util::usesInTheImplementationExists("D", "C", session));
0468     REQUIRE(!Test_Util::usesInTheInterfaceExists("D", "C", session));
0469 }
0470 
0471 TEST_CASE("Namespace var decl")
0472 {
0473     static const char *source = R"(
0474 class C;
0475 
0476 namespace foo {
0477 
0478 C *c;
0479 
0480 }
0481 )";
0482     ObjectStore session;
0483     REQUIRE(Test_Util::runOnCode(session, source, "testNamespaceVarDecl.cpp"));
0484 
0485     NamespaceObject *foo = nullptr;
0486     VariableObject *c = nullptr;
0487 
0488     session.withROLock([&] {
0489         foo = session.getNamespace("foo");
0490         c = session.getVariable("foo::c");
0491     });
0492     REQUIRE(foo);
0493     REQUIRE(c);
0494 
0495     c->withROLock([&] {
0496         REQUIRE(c->name() == "c");
0497         REQUIRE(c->parent() == foo);
0498         REQUIRE(!c->isGlobal());
0499         REQUIRE(c->signature() == "C *");
0500     });
0501 }
0502 
0503 TEST_CASE("Global var decl")
0504 {
0505     static const char *source = R"(
0506 class C;
0507 
0508 C *c;
0509 )";
0510     ObjectStore session;
0511     REQUIRE(Test_Util::runOnCode(session, source, "testGlobalVarDecl.cpp"));
0512 
0513     VariableObject *c = nullptr;
0514 
0515     session.withROLock([&] {
0516         c = session.getVariable("c");
0517     });
0518     REQUIRE(c);
0519 
0520     c->withROLock([&] {
0521         REQUIRE(c->name() == "c");
0522         REQUIRE(!c->parent());
0523         REQUIRE(c->isGlobal());
0524         REQUIRE(c->signature() == "C *");
0525     });
0526 }
0527 
0528 TEST_CASE("Constexpr global var")
0529 {
0530     static const char *source = R"(
0531 namespace foo {
0532 
0533 constexpr double pi = 3.141592654;
0534 
0535 }
0536 )";
0537     ObjectStore session;
0538     REQUIRE(Test_Util::runOnCode(session, source, "testConstexprGlobalVar.cpp"));
0539 
0540     NamespaceObject *foo = nullptr;
0541     VariableObject *pi = nullptr;
0542 
0543     session.withROLock([&] {
0544         foo = session.getNamespace("foo");
0545         pi = session.getVariable("foo::pi");
0546     });
0547     REQUIRE(foo);
0548     REQUIRE(pi);
0549 
0550     pi->withROLock([&] {
0551         REQUIRE(pi->name() == "pi");
0552         REQUIRE(pi->parent() == foo);
0553         REQUIRE(!pi->isGlobal());
0554         REQUIRE(pi->signature() == "const double");
0555     });
0556 }