File indexing completed on 2024-05-19 05:42:04
0001 // ct_lvtclp_testfunction.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 VisitFunctionDecl and VisitCXXMethodDecl 0021 0022 #include <ct_lvtmdb_functionobject.h> 0023 #include <ct_lvtmdb_methodobject.h> 0024 #include <ct_lvtmdb_namespaceobject.h> 0025 #include <ct_lvtmdb_objectstore.h> 0026 #include <ct_lvtmdb_typeobject.h> 0027 0028 #include <ct_lvtshr_graphenums.h> 0029 0030 #include <ct_lvtclp_testutil.h> 0031 0032 #include <initializer_list> 0033 #include <iostream> 0034 0035 #include <catch2-local-includes.h> 0036 #include <clang/Basic/Version.h> 0037 0038 using namespace Codethink::lvtclp; 0039 using namespace Codethink::lvtmdb; 0040 using namespace Codethink; 0041 0042 const PyDefaultGilReleasedContext defaultGilContextForTesting; 0043 0044 TEST_CASE("Function declaration") 0045 { 0046 const static char *source = R"( 0047 namespace foo { 0048 0049 int function(int arg0, const int& arg1) 0050 { 0051 if (arg0) 0052 return arg1; 0053 return -arg1; 0054 } 0055 0056 } 0057 )"; 0058 0059 ObjectStore session; 0060 REQUIRE(Test_Util::runOnCode(session, source, "testFunctionDecl.cpp")); 0061 0062 NamespaceObject *foo; 0063 FunctionObject *fn = nullptr; 0064 0065 session.withROLock([&] { 0066 foo = session.getNamespace("foo"); 0067 fn = session.getFunction("foo::function", "function(int arg0, const int & arg1)", std::string{}, "int"); 0068 }); 0069 0070 REQUIRE(foo); 0071 REQUIRE(fn); 0072 0073 fn->withROLock([&] { 0074 REQUIRE(fn->name() == "function"); 0075 REQUIRE(fn->signature() == "function(int arg0, const int & arg1)"); 0076 REQUIRE(fn->returnType() == "int"); 0077 REQUIRE(fn->templateParameters().empty()); 0078 REQUIRE(fn->parent() == foo); 0079 }); 0080 } 0081 0082 TEST_CASE("Static function") 0083 { 0084 const static char *source = R"( 0085 static void function() 0086 { 0087 } 0088 )"; 0089 ObjectStore session; 0090 REQUIRE(Test_Util::runOnCode(session, source, "testStaticFn.cpp")); 0091 0092 session.withROLock([&] { 0093 REQUIRE(session.functions().size() == 1); 0094 }); 0095 } 0096 0097 TEST_CASE("Function overload") 0098 { 0099 const static char *source = R"( 0100 int foo() 0101 { 0102 return 2; 0103 } 0104 0105 int foo(int arg) 0106 { 0107 return arg; 0108 } 0109 )"; 0110 ObjectStore session; 0111 REQUIRE(Test_Util::runOnCode(session, source, "testFuncOverload.cpp")); 0112 FunctionObject *foo0 = nullptr; 0113 FunctionObject *foo1 = nullptr; 0114 0115 session.withROLock([&] { 0116 foo0 = session.getFunction("foo", "foo()", std::string{}, "int"); 0117 foo1 = session.getFunction("foo", "foo(int arg)", std::string{}, "int"); 0118 }); 0119 0120 REQUIRE(foo0); 0121 REQUIRE(foo1); 0122 0123 for (const auto& fn : {foo0, foo1}) { 0124 auto lock = fn->readOnlyLock(); 0125 REQUIRE(fn->qualifiedName() == "foo"); 0126 REQUIRE(fn->returnType() == "int"); 0127 REQUIRE(fn->templateParameters().empty()); 0128 REQUIRE(!fn->parent()); 0129 } 0130 } 0131 0132 TEST_CASE("Function forward declaration") 0133 { 0134 const static char *source = R"( 0135 void function(); 0136 0137 void function() 0138 { 0139 } 0140 )"; 0141 0142 ObjectStore session; 0143 REQUIRE(Test_Util::runOnCode(session, source, "testFnForwardDecl.cpp")); 0144 0145 session.withROLock([&] { 0146 // REQUIRE(session.getFunction("function")); 0147 // REQUIRE(func); 0148 0149 // check we didn't add any kind of second function for the forward decl 0150 REQUIRE(session.functions().size() == 1); 0151 }); 0152 } 0153 0154 TEST_CASE("Function template") 0155 { 0156 const static char *source = R"( 0157 template <typename T> 0158 T clone(const T& t) 0159 { 0160 return t; 0161 } 0162 0163 void instantiateSpecialization() 0164 { 0165 int i = 2; 0166 int j = clone(i); 0167 int k = clone<int>(i); 0168 } 0169 )"; 0170 ObjectStore session; 0171 REQUIRE(Test_Util::runOnCode(session, source, "testFnTemplate.cpp")); 0172 0173 FunctionObject *clone = nullptr; 0174 session.withROLock([&] { 0175 clone = session.getFunction("clone", "clone(const T & t)", "template <typename T>", "type-parameter-0-0"); 0176 0177 // check we didn't separately add clone<int> 0178 REQUIRE(session.functions().size() == 2); 0179 }); 0180 0181 REQUIRE(clone); 0182 0183 clone->withROLock([&] { 0184 REQUIRE(clone->templateParameters() == "template <typename T>"); 0185 }); 0186 } 0187 0188 TEST_CASE("Function default parameters") 0189 { 0190 const static char *source = R"( 0191 namespace foo { 0192 0193 int function(int &arg1, const int arg0 = 0) 0194 { 0195 if (arg0) 0196 return arg1; 0197 return -arg1; 0198 } 0199 0200 } 0201 )"; 0202 ObjectStore session; 0203 REQUIRE(Test_Util::runOnCode(session, source, "testFnDefaultParams.cpp")); 0204 0205 NamespaceObject *foo = nullptr; 0206 FunctionObject *fn = nullptr; 0207 session.withROLock([&] { 0208 foo = session.getNamespace("foo"); 0209 fn = session.getFunction("foo::function", "function(int & arg1, const int arg0 = 0)", std::string{}, "int"); 0210 }); 0211 0212 REQUIRE(foo); 0213 REQUIRE(fn); 0214 0215 fn->withROLock([&] { 0216 REQUIRE(fn); 0217 REQUIRE(fn->name() == "function"); 0218 REQUIRE(fn->signature() == "function(int & arg1, const int arg0 = 0)"); 0219 REQUIRE(fn->returnType() == "int"); 0220 REQUIRE(fn->templateParameters().empty()); 0221 REQUIRE(fn->parent() == foo); 0222 }); 0223 } 0224 0225 TEST_CASE("Method declaration") 0226 { 0227 const static char *source = R"( 0228 class C { 0229 void method(); 0230 }; 0231 0232 void C::method() 0233 { 0234 } 0235 )"; 0236 ObjectStore session; 0237 REQUIRE(Test_Util::runOnCode(session, source, "testMethodDeclaration.cpp")); 0238 0239 TypeObject *C = nullptr; 0240 MethodObject *method = nullptr; 0241 0242 session.withROLock([&] { 0243 C = session.getType("C"); 0244 method = session.getMethod("C::method", "method()", std::string{}, "void"); 0245 }); 0246 REQUIRE(method); 0247 0248 method->withROLock([&] { 0249 REQUIRE(method->name() == "method"); 0250 REQUIRE(method->signature() == "method()"); 0251 REQUIRE(method->returnType() == "void"); 0252 REQUIRE(method->templateParameters().empty()); 0253 REQUIRE(!method->isVirtual()); 0254 REQUIRE(!method->isPure()); 0255 REQUIRE(!method->isStatic()); 0256 REQUIRE(!method->isConst()); 0257 REQUIRE(method->parent() == C); 0258 REQUIRE(method->argumentTypes().empty()); 0259 }); 0260 } 0261 0262 TEST_CASE("Method access") 0263 { 0264 const static char *source = R"( 0265 class C { 0266 public: 0267 void pub() {} 0268 0269 protected: 0270 void prot() {} 0271 0272 private: 0273 void priv() {} 0274 }; 0275 )"; 0276 ObjectStore session; 0277 REQUIRE(Test_Util::runOnCode(session, source, "testMethodAccess.cpp")); 0278 0279 MethodObject *pub = nullptr; 0280 MethodObject *prot = nullptr; 0281 MethodObject *priv = nullptr; 0282 0283 session.withROLock([&] { 0284 pub = session.getMethod("C::pub", "pub()", std::string{}, "void"); 0285 prot = session.getMethod("C::prot", "prot()", std::string{}, "void"); 0286 priv = session.getMethod("C::priv", "priv()", std::string{}, "void"); 0287 }); 0288 REQUIRE(pub); 0289 REQUIRE(prot); 0290 REQUIRE(priv); 0291 0292 pub->withROLock([&] { 0293 REQUIRE(pub->access() == lvtshr::AccessSpecifier::e_public); 0294 }); 0295 prot->withROLock([&] { 0296 REQUIRE(prot->access() == lvtshr::AccessSpecifier::e_protected); 0297 }); 0298 priv->withROLock([&] { 0299 REQUIRE(priv->access() == lvtshr::AccessSpecifier::e_private); 0300 }); 0301 } 0302 0303 TEST_CASE("Method specifiers") 0304 { 0305 const static char *source = R"( 0306 class C { 0307 virtual void virtMethod() {} 0308 virtual void pureMethod() = 0; 0309 static void staticMethod() {} 0310 void constMethod() const {} 0311 }; 0312 )"; 0313 ObjectStore session; 0314 REQUIRE(Test_Util::runOnCode(session, source, "testMethodSpecifiers.cpp")); 0315 0316 MethodObject *virtMethod = nullptr; 0317 MethodObject *pureMethod = nullptr; 0318 MethodObject *staticMethod = nullptr; 0319 MethodObject *constMethod = nullptr; 0320 0321 session.withROLock([&] { 0322 virtMethod = session.getMethod("C::virtMethod", "virtMethod()", std::string{}, "void"); 0323 pureMethod = session.getMethod("C::pureMethod", "pureMethod()", std::string{}, "void"); 0324 staticMethod = session.getMethod("C::staticMethod", "staticMethod()", std::string{}, "void"); 0325 constMethod = session.getMethod("C::constMethod", "constMethod()", std::string{}, "void"); 0326 }); 0327 0328 REQUIRE(virtMethod); 0329 REQUIRE(pureMethod); 0330 REQUIRE(staticMethod); 0331 REQUIRE(constMethod); 0332 0333 virtMethod->withROLock([&] { 0334 REQUIRE(virtMethod->isVirtual()); 0335 REQUIRE(!virtMethod->isPure()); 0336 REQUIRE(!virtMethod->isStatic()); 0337 REQUIRE(!virtMethod->isConst()); 0338 }); 0339 0340 pureMethod->withRWLock([&] { 0341 REQUIRE(pureMethod->isVirtual()); 0342 REQUIRE(pureMethod->isPure()); 0343 REQUIRE(!pureMethod->isStatic()); 0344 REQUIRE(!pureMethod->isConst()); 0345 }); 0346 0347 staticMethod->withROLock([&] { 0348 REQUIRE(!staticMethod->isVirtual()); 0349 REQUIRE(!staticMethod->isPure()); 0350 REQUIRE(staticMethod->isStatic()); 0351 REQUIRE(!staticMethod->isConst()); 0352 }); 0353 0354 constMethod->withROLock([&] { 0355 REQUIRE(!constMethod->isVirtual()); 0356 REQUIRE(!constMethod->isPure()); 0357 REQUIRE(!constMethod->isStatic()); 0358 REQUIRE(constMethod->isConst()); 0359 }); 0360 } 0361 0362 TEST_CASE("Method arguments") 0363 { 0364 const static char *source = R"( 0365 class C {}; 0366 class D { 0367 void method(int i, const C& c) {} 0368 }; 0369 )"; 0370 ObjectStore session; 0371 REQUIRE(Test_Util::runOnCode(session, source, "testMethodArguments.cpp")); 0372 0373 MethodObject *method = nullptr; 0374 session.withROLock([&] { 0375 method = session.getMethod("D::method", "method(int i, const C & c)", std::string{}, "void"); 0376 }); 0377 REQUIRE(method); 0378 0379 method->withROLock([&] { 0380 const auto argumentClasses = method->argumentTypes(); 0381 REQUIRE(argumentClasses.size() == 1); 0382 auto *C = *argumentClasses.begin(); 0383 0384 C->withROLock([&] { 0385 REQUIRE(C->qualifiedName() == "C"); 0386 }); 0387 }); 0388 } 0389 0390 TEST_CASE("Method overload") 0391 { 0392 const static char *source = R"( 0393 class C {}; 0394 0395 class D { 0396 int foo () 0397 { 0398 return 2; 0399 } 0400 0401 int foo(C arg) 0402 { 0403 return 2; 0404 } 0405 }; 0406 )"; 0407 ObjectStore session; 0408 REQUIRE(Test_Util::runOnCode(session, source, "testMethodOverload.cpp")); 0409 0410 TypeObject *C = nullptr; 0411 TypeObject *D = nullptr; 0412 MethodObject *foo0 = nullptr; // method without parameters 0413 MethodObject *foo1 = nullptr; // method with parameters 0414 session.withROLock([&] { 0415 C = session.getType("C"); 0416 D = session.getType("D"); 0417 foo0 = session.getMethod("D::foo", "foo()", std::string{}, "int"); 0418 foo1 = session.getMethod("D::foo", "foo(C arg)", std::string{}, "int"); 0419 }); 0420 0421 REQUIRE(C); 0422 REQUIRE(D); 0423 REQUIRE(foo0); 0424 REQUIRE(foo1); 0425 0426 for (const auto& foo : {foo0, foo1}) { 0427 foo->withROLock([&] { 0428 REQUIRE(foo->qualifiedName() == "D::foo"); 0429 REQUIRE(foo->returnType() == "int"); 0430 REQUIRE(foo->templateParameters().empty()); 0431 REQUIRE(foo->access() == lvtshr::AccessSpecifier::e_private); 0432 REQUIRE(!foo->isVirtual()); 0433 REQUIRE(!foo->isPure()); 0434 REQUIRE(!foo->isStatic()); 0435 REQUIRE(!foo->isConst()); 0436 REQUIRE(foo->parent() == D); 0437 }); 0438 } 0439 0440 foo0->withROLock([&] { 0441 REQUIRE(foo0->argumentTypes().empty()); 0442 }); 0443 0444 foo1->withROLock([&] { 0445 const auto foo1Args = foo1->argumentTypes(); 0446 REQUIRE(foo1Args.size() == 1); 0447 REQUIRE(*foo1Args.begin() == C); 0448 }); 0449 } 0450 0451 TEST_CASE("Method template") 0452 { 0453 const static char *source = R"( 0454 class C { 0455 public: 0456 template <typename T> 0457 T clone(const T& t) 0458 { 0459 return t; 0460 } 0461 }; 0462 0463 void instantiateSpecialization() 0464 { 0465 C c; 0466 int i = 2; 0467 int j = c.clone(i); 0468 } 0469 )"; 0470 ObjectStore session; 0471 REQUIRE(Test_Util::runOnCode(session, source, "testMethodTemplate.cpp")); 0472 0473 MethodObject *clone = nullptr; 0474 session.withROLock([&] { 0475 for (const auto& [_, fn] : session.methods()) { 0476 std::cout << "Key = --- \n" << _ << "\n ---\n"; 0477 } 0478 0479 clone = session.getMethod("C::clone", "clone(const T & t)", "template <typename T>", "type-parameter-0-0"); 0480 0481 // check we didn't separately add C::clone<int> 0482 REQUIRE(session.functions().size() == 1); 0483 }); 0484 REQUIRE(clone); 0485 0486 clone->withROLock([&] { 0487 REQUIRE(clone->templateParameters() == "template <typename T>"); 0488 }); 0489 } 0490 0491 TEST_CASE("Method with default param") 0492 { 0493 const static char *source = R"( 0494 class C { 0495 void method(int i = 0); 0496 }; 0497 0498 void C::method(int i) 0499 { 0500 } 0501 )"; 0502 ObjectStore session; 0503 REQUIRE(Test_Util::runOnCode(session, source, "testMethodDefaultParam.cpp")); 0504 0505 TypeObject *C = nullptr; 0506 MethodObject *method = nullptr; 0507 0508 session.withROLock([&] { 0509 C = session.getType("C"); 0510 method = session.getMethod("C::method", "method(int i = 0)", std::string{}, "void"); 0511 }); 0512 REQUIRE(C); 0513 REQUIRE(method); 0514 0515 method->withROLock([&] { 0516 REQUIRE(method->name() == "method"); 0517 REQUIRE(method->signature() == "method(int i = 0)"); 0518 REQUIRE(method->returnType() == "void"); 0519 REQUIRE(method->templateParameters().empty()); 0520 REQUIRE(!method->isVirtual()); 0521 REQUIRE(!method->isPure()); 0522 REQUIRE(!method->isStatic()); 0523 REQUIRE(!method->isConst()); 0524 REQUIRE(method->parent() == C); 0525 REQUIRE(method->argumentTypes().empty()); 0526 }); 0527 } 0528 0529 TEST_CASE("Dependency via free function") 0530 { 0531 // We need both C and CFactory because variable declarations and static 0532 // function calls are tracked in different places in CodebaseDbVisitor 0533 const static char *source = R"( 0534 class C { 0535 public: 0536 void method(char c); 0537 }; 0538 0539 class CFactory { 0540 public: 0541 static C makeC(); 0542 }; 0543 0544 static void freeFunction(char ch) 0545 { 0546 C c = CFactory::makeC(); 0547 c.method(ch); 0548 } 0549 0550 class D { 0551 void method() 0552 { 0553 freeFunction('D'); 0554 } 0555 }; 0556 0557 class E { 0558 void method() 0559 { 0560 freeFunction('E'); 0561 } 0562 }; 0563 )"; 0564 ObjectStore session; 0565 REQUIRE(Test_Util::runOnCode(session, source, "testDepViaFreeFunction.cpp")); 0566 0567 REQUIRE(Test_Util::usesInTheImplementationExists("D", "CFactory", session)); 0568 REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session)); 0569 REQUIRE(Test_Util::usesInTheImplementationExists("E", "CFactory", session)); 0570 REQUIRE(Test_Util::usesInTheImplementationExists("E", "C", session)); 0571 } 0572 0573 TEST_CASE("Dependency via function") 0574 { 0575 const static char *source = R"( 0576 class C { 0577 public: 0578 void method(); 0579 }; 0580 0581 static void func1() 0582 { 0583 C c; 0584 c.method(); 0585 } 0586 0587 static void func2() 0588 { 0589 func1(); 0590 } 0591 0592 class D { 0593 void method() 0594 { 0595 func2(); 0596 } 0597 }; 0598 )"; 0599 ObjectStore session; 0600 REQUIRE(Test_Util::runOnCode(session, source, "testDepViaFunctionViaFunction.cpp")); 0601 0602 REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session)); 0603 } 0604 0605 TEST_CASE("Circular function dependency") 0606 { 0607 const static char *source = R"( 0608 class C {}; 0609 class D {}; 0610 0611 static void funcC(bool b); 0612 static void funcD(bool b); 0613 0614 static void funcC(bool b) 0615 { 0616 C c; 0617 if (b) { 0618 funcD(false); 0619 } 0620 } 0621 0622 static void funcD(bool b) 0623 { 0624 D d; 0625 if (b) { 0626 funcC(false); 0627 } 0628 } 0629 0630 class E { 0631 void method() 0632 { 0633 funcC(true); 0634 }; 0635 }; 0636 )"; 0637 ObjectStore session; 0638 REQUIRE(Test_Util::runOnCode(session, source, "testDepViaCircularFunctionDep.cpp")); 0639 0640 REQUIRE(Test_Util::usesInTheImplementationExists("E", "C", session)); 0641 REQUIRE(Test_Util::usesInTheImplementationExists("E", "D", session)); 0642 } 0643 0644 TEST_CASE("Tricky function expansion") 0645 { 0646 // This is a bad test. It relies on the unordered_multimap in staticfnhandler 0647 // traversing x then y then z. On clang+gnu(?)+linux this seems to work by 0648 // the order functions appear in the file 0649 static const char *source = R"( 0650 class C {}; 0651 0652 static void x(); 0653 static void y(); 0654 static void z(); 0655 0656 static void x() 0657 { 0658 C c; 0659 } 0660 0661 static void y() 0662 { 0663 x(); 0664 } 0665 0666 static void z() 0667 { 0668 y(); 0669 } 0670 0671 class D { 0672 void method() 0673 { 0674 z(); 0675 } 0676 }; 0677 )"; 0678 ObjectStore session; 0679 REQUIRE(Test_Util::runOnCode(session, source, "testTrickyFUnctionExpansion.cpp")); 0680 0681 REQUIRE(Test_Util::usesInTheImplementationExists("D", "C", session)); 0682 } 0683 0684 TEST_CASE("Function dependency overload") 0685 { 0686 // check that the code for detecting uses-in-impl relationships via static 0687 // free functions isn't confused by overloaded functions 0688 static const char *source = R"( 0689 class C {}; 0690 class D {}; 0691 0692 static void freeFunction(int arg) 0693 { 0694 C c; 0695 } 0696 0697 static void freeFunction(bool arg) 0698 { 0699 D d; 0700 } 0701 0702 class UsesC { 0703 void method() 0704 { 0705 freeFunction(42); 0706 } 0707 }; 0708 0709 class UsesD { 0710 void method() 0711 { 0712 freeFunction(true); 0713 } 0714 }; 0715 )"; 0716 ObjectStore session; 0717 REQUIRE(Test_Util::runOnCode(session, source, "testFunctionDepOverload.cpp")); 0718 0719 REQUIRE(Test_Util::usesInTheImplementationExists("UsesC", "C", session)); 0720 REQUIRE(Test_Util::usesInTheImplementationExists("UsesD", "D", session)); 0721 REQUIRE(!Test_Util::usesInTheImplementationExists("UsesC", "D", session)); 0722 REQUIRE(!Test_Util::usesInTheImplementationExists("UsesD", "C", session)); 0723 } 0724 0725 // TODO: Review this. Should an indirect call to a static class member through a free function infer a direct 0726 // dependency? I think no. But the original writer of this test believes yes. Perhaps because at the time, we didn't 0727 // have the free functions as entities, so it kind of make sense, but now that we do have them, I think the indirect 0728 // dependency should be enough. The same applies for the test above, which is _not_ failing, but I believe it should 0729 // fail. All that must be revisited. 0730 // 0731 // clang-format off 0732 //TEST_CASE("Function dependency with template") 0733 //{ 0734 // // check that the code for detecting uses-in-impl relationships via static 0735 // // free functions isn't confused by template specializations 0736 // static const char *source = R"( 0737 //class C {}; 0738 //class D { public: static void method(); }; 0739 // 0740 //template <class TYPE> 0741 //static void freeFunction(); 0742 // 0743 //template <> 0744 //void freeFunction<int>() 0745 //{ 0746 // // test CodebaseDbVisitor::visitLocalVarDeclOrParam path 0747 // C c; 0748 //} 0749 // 0750 //template <> 0751 //void freeFunction<bool>() 0752 //{ 0753 // // test CodebaseDbVisitor::searchClangStmtAST path 0754 // D::method(); 0755 //} 0756 // 0757 //class UsesC { 0758 // void method() 0759 // { 0760 // freeFunction<int>(); 0761 // } 0762 //}; 0763 // 0764 //class UsesD { 0765 // void method() 0766 // { 0767 // freeFunction<bool>(); 0768 // } 0769 //}; 0770 //)"; 0771 // ObjectStore session; 0772 // REQUIRE(Test_Util::runOnCode(session, source, "testFunctionDepTemplate.cpp")); 0773 // 0774 // REQUIRE(Test_Util::usesInTheImplementationExists("UsesC", "C", session)); 0775 // REQUIRE(Test_Util::usesInTheImplementationExists("UsesD", "D", session)); 0776 // REQUIRE(!Test_Util::usesInTheImplementationExists("UsesC", "D", session)); 0777 // REQUIRE(!Test_Util::usesInTheImplementationExists("UsesD", "C", session)); 0778 //} 0779 // clang-format on