1275196d8Smartinboehme //===- unittests/Analysis/FlowSensitive/ASTOpsTest.cpp --------------------===// 2275196d8Smartinboehme // 3275196d8Smartinboehme // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4275196d8Smartinboehme // See https://llvm.org/LICENSE.txt for license information. 5275196d8Smartinboehme // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6275196d8Smartinboehme // 7275196d8Smartinboehme //===----------------------------------------------------------------------===// 8275196d8Smartinboehme 9275196d8Smartinboehme #include "clang/Analysis/FlowSensitive/ASTOps.h" 10275196d8Smartinboehme #include "TestingSupport.h" 11*198fb5edSSamira Bazuzi #include "clang/AST/Decl.h" 12*198fb5edSSamira Bazuzi #include "clang/AST/DeclCXX.h" 13*198fb5edSSamira Bazuzi #include "clang/ASTMatchers/ASTMatchers.h" 14*198fb5edSSamira Bazuzi #include "clang/Basic/LLVM.h" 15*198fb5edSSamira Bazuzi #include "clang/Frontend/ASTUnit.h" 16*198fb5edSSamira Bazuzi #include "clang/Tooling/Tooling.h" 17275196d8Smartinboehme #include "gmock/gmock.h" 18275196d8Smartinboehme #include "gtest/gtest.h" 19275196d8Smartinboehme #include <memory> 20*198fb5edSSamira Bazuzi #include <string> 21275196d8Smartinboehme 22275196d8Smartinboehme namespace { 23275196d8Smartinboehme 24275196d8Smartinboehme using namespace clang; 25275196d8Smartinboehme using namespace dataflow; 26275196d8Smartinboehme 27*198fb5edSSamira Bazuzi using ast_matchers::cxxMethodDecl; 28275196d8Smartinboehme using ast_matchers::cxxRecordDecl; 29275196d8Smartinboehme using ast_matchers::hasName; 30275196d8Smartinboehme using ast_matchers::hasType; 31275196d8Smartinboehme using ast_matchers::initListExpr; 32275196d8Smartinboehme using ast_matchers::match; 33275196d8Smartinboehme using ast_matchers::selectFirst; 34275196d8Smartinboehme using test::findValueDecl; 35275196d8Smartinboehme using testing::IsEmpty; 36275196d8Smartinboehme using testing::UnorderedElementsAre; 37275196d8Smartinboehme 38275196d8Smartinboehme TEST(ASTOpsTest, RecordInitListHelperOnEmptyUnionInitList) { 39275196d8Smartinboehme // This is a regression test: The `RecordInitListHelper` used to assert-fail 40275196d8Smartinboehme // when called for the `InitListExpr` of an empty union. 41275196d8Smartinboehme std::string Code = R"cc( 42275196d8Smartinboehme struct S { 43275196d8Smartinboehme S() : UField{} {}; 44275196d8Smartinboehme 45275196d8Smartinboehme union U {} UField; 46275196d8Smartinboehme }; 47275196d8Smartinboehme )cc"; 48275196d8Smartinboehme std::unique_ptr<ASTUnit> Unit = 49275196d8Smartinboehme tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 50275196d8Smartinboehme auto &ASTCtx = Unit->getASTContext(); 51275196d8Smartinboehme 52275196d8Smartinboehme ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 53275196d8Smartinboehme 54275196d8Smartinboehme auto *InitList = selectFirst<InitListExpr>( 55275196d8Smartinboehme "init", 56275196d8Smartinboehme match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), 57275196d8Smartinboehme ASTCtx)); 58275196d8Smartinboehme ASSERT_NE(InitList, nullptr); 59275196d8Smartinboehme 60275196d8Smartinboehme RecordInitListHelper Helper(InitList); 61275196d8Smartinboehme EXPECT_THAT(Helper.base_inits(), IsEmpty()); 62275196d8Smartinboehme EXPECT_THAT(Helper.field_inits(), IsEmpty()); 63275196d8Smartinboehme } 64275196d8Smartinboehme 65275196d8Smartinboehme TEST(ASTOpsTest, ReferencedDeclsOnUnionInitList) { 66275196d8Smartinboehme // This is a regression test: `getReferencedDecls()` used to return a null 67275196d8Smartinboehme // `FieldDecl` in this case (in addition to the correct non-null `FieldDecl`) 68275196d8Smartinboehme // because `getInitializedFieldInUnion()` returns null for the syntactic form 69275196d8Smartinboehme // of the `InitListExpr`. 70275196d8Smartinboehme std::string Code = R"cc( 71275196d8Smartinboehme struct S { 72275196d8Smartinboehme S() : UField{0} {}; 73275196d8Smartinboehme 74275196d8Smartinboehme union U { 75275196d8Smartinboehme int I; 76275196d8Smartinboehme } UField; 77275196d8Smartinboehme }; 78275196d8Smartinboehme )cc"; 79275196d8Smartinboehme std::unique_ptr<ASTUnit> Unit = 80275196d8Smartinboehme tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 81275196d8Smartinboehme auto &ASTCtx = Unit->getASTContext(); 82275196d8Smartinboehme 83275196d8Smartinboehme ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 84275196d8Smartinboehme 85275196d8Smartinboehme auto *InitList = selectFirst<InitListExpr>( 86275196d8Smartinboehme "init", 87275196d8Smartinboehme match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), 88275196d8Smartinboehme ASTCtx)); 89275196d8Smartinboehme ASSERT_NE(InitList, nullptr); 90275196d8Smartinboehme auto *IDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "I")); 91275196d8Smartinboehme 92275196d8Smartinboehme EXPECT_THAT(getReferencedDecls(*InitList).Fields, 93275196d8Smartinboehme UnorderedElementsAre(IDecl)); 94275196d8Smartinboehme } 95275196d8Smartinboehme 962575ea6eSSamira Bazuzi TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) { 972575ea6eSSamira Bazuzi std::string Code = R"cc( 982575ea6eSSamira Bazuzi void func(int Param) { 992575ea6eSSamira Bazuzi static int Static = 0; 1002575ea6eSSamira Bazuzi int Local = Param; 1012575ea6eSSamira Bazuzi Local = Static; 1022575ea6eSSamira Bazuzi } 1032575ea6eSSamira Bazuzi )cc"; 1042575ea6eSSamira Bazuzi std::unique_ptr<ASTUnit> Unit = 1052575ea6eSSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 1062575ea6eSSamira Bazuzi auto &ASTCtx = Unit->getASTContext(); 1072575ea6eSSamira Bazuzi 1082575ea6eSSamira Bazuzi ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 1092575ea6eSSamira Bazuzi 1102575ea6eSSamira Bazuzi auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func")); 1112575ea6eSSamira Bazuzi ASSERT_NE(Func, nullptr); 1122575ea6eSSamira Bazuzi auto *LocalDecl = cast<VarDecl>(findValueDecl(ASTCtx, "Local")); 1132575ea6eSSamira Bazuzi 1142575ea6eSSamira Bazuzi EXPECT_THAT(getReferencedDecls(*Func).Locals, 1152575ea6eSSamira Bazuzi UnorderedElementsAre(LocalDecl)); 1162575ea6eSSamira Bazuzi } 1172575ea6eSSamira Bazuzi 118*198fb5edSSamira Bazuzi TEST(ASTOpsTest, LambdaCaptures) { 119*198fb5edSSamira Bazuzi std::string Code = R"cc( 120*198fb5edSSamira Bazuzi void func(int CapturedByRef, int CapturedByValue, int NotCaptured) { 121*198fb5edSSamira Bazuzi int Local; 122*198fb5edSSamira Bazuzi auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) { 123*198fb5edSSamira Bazuzi }; 124*198fb5edSSamira Bazuzi } 125*198fb5edSSamira Bazuzi )cc"; 126*198fb5edSSamira Bazuzi std::unique_ptr<ASTUnit> Unit = 127*198fb5edSSamira Bazuzi tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 128*198fb5edSSamira Bazuzi auto &ASTCtx = Unit->getASTContext(); 129*198fb5edSSamira Bazuzi 130*198fb5edSSamira Bazuzi ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 131*198fb5edSSamira Bazuzi 132*198fb5edSSamira Bazuzi auto *LambdaCallOp = selectFirst<CXXMethodDecl>( 133*198fb5edSSamira Bazuzi "l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx)); 134*198fb5edSSamira Bazuzi ASSERT_NE(LambdaCallOp, nullptr); 135*198fb5edSSamira Bazuzi auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func")); 136*198fb5edSSamira Bazuzi ASSERT_NE(Func, nullptr); 137*198fb5edSSamira Bazuzi auto *CapturedByRefDecl = Func->getParamDecl(0); 138*198fb5edSSamira Bazuzi ASSERT_NE(CapturedByRefDecl, nullptr); 139*198fb5edSSamira Bazuzi auto *CapturedByValueDecl = Func->getParamDecl(1); 140*198fb5edSSamira Bazuzi ASSERT_NE(CapturedByValueDecl, nullptr); 141*198fb5edSSamira Bazuzi 142*198fb5edSSamira Bazuzi EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty()); 143*198fb5edSSamira Bazuzi ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp); 144*198fb5edSSamira Bazuzi EXPECT_THAT(ForLambda.LambdaCapturedParams, 145*198fb5edSSamira Bazuzi UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl)); 146*198fb5edSSamira Bazuzi // Captured locals must be seen in the body for them to appear in 147*198fb5edSSamira Bazuzi // ReferencedDecls. 148*198fb5edSSamira Bazuzi EXPECT_THAT(ForLambda.Locals, IsEmpty()); 149*198fb5edSSamira Bazuzi } 150*198fb5edSSamira Bazuzi 151275196d8Smartinboehme } // namespace 152