1 //===- unittests/Analysis/FlowSensitive/ASTOpsTest.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 "clang/Analysis/FlowSensitive/ASTOps.h" 10 #include "TestingSupport.h" 11 #include "clang/AST/Decl.h" 12 #include "clang/AST/DeclCXX.h" 13 #include "clang/ASTMatchers/ASTMatchers.h" 14 #include "clang/Basic/LLVM.h" 15 #include "clang/Frontend/ASTUnit.h" 16 #include "clang/Tooling/Tooling.h" 17 #include "gmock/gmock.h" 18 #include "gtest/gtest.h" 19 #include <memory> 20 #include <string> 21 22 namespace { 23 24 using namespace clang; 25 using namespace dataflow; 26 27 using ast_matchers::cxxMethodDecl; 28 using ast_matchers::cxxRecordDecl; 29 using ast_matchers::hasName; 30 using ast_matchers::hasType; 31 using ast_matchers::initListExpr; 32 using ast_matchers::match; 33 using ast_matchers::selectFirst; 34 using test::findValueDecl; 35 using testing::IsEmpty; 36 using testing::UnorderedElementsAre; 37 38 TEST(ASTOpsTest, RecordInitListHelperOnEmptyUnionInitList) { 39 // This is a regression test: The `RecordInitListHelper` used to assert-fail 40 // when called for the `InitListExpr` of an empty union. 41 std::string Code = R"cc( 42 struct S { 43 S() : UField{} {}; 44 45 union U {} UField; 46 }; 47 )cc"; 48 std::unique_ptr<ASTUnit> Unit = 49 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 50 auto &ASTCtx = Unit->getASTContext(); 51 52 ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 53 54 auto *InitList = selectFirst<InitListExpr>( 55 "init", 56 match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), 57 ASTCtx)); 58 ASSERT_NE(InitList, nullptr); 59 60 RecordInitListHelper Helper(InitList); 61 EXPECT_THAT(Helper.base_inits(), IsEmpty()); 62 EXPECT_THAT(Helper.field_inits(), IsEmpty()); 63 } 64 65 TEST(ASTOpsTest, ReferencedDeclsOnUnionInitList) { 66 // This is a regression test: `getReferencedDecls()` used to return a null 67 // `FieldDecl` in this case (in addition to the correct non-null `FieldDecl`) 68 // because `getInitializedFieldInUnion()` returns null for the syntactic form 69 // of the `InitListExpr`. 70 std::string Code = R"cc( 71 struct S { 72 S() : UField{0} {}; 73 74 union U { 75 int I; 76 } UField; 77 }; 78 )cc"; 79 std::unique_ptr<ASTUnit> Unit = 80 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 81 auto &ASTCtx = Unit->getASTContext(); 82 83 ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 84 85 auto *InitList = selectFirst<InitListExpr>( 86 "init", 87 match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), 88 ASTCtx)); 89 ASSERT_NE(InitList, nullptr); 90 auto *IDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "I")); 91 92 EXPECT_THAT(getReferencedDecls(*InitList).Fields, 93 UnorderedElementsAre(IDecl)); 94 } 95 96 TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) { 97 std::string Code = R"cc( 98 void func(int Param) { 99 static int Static = 0; 100 int Local = Param; 101 Local = Static; 102 } 103 )cc"; 104 std::unique_ptr<ASTUnit> Unit = 105 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 106 auto &ASTCtx = Unit->getASTContext(); 107 108 ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 109 110 auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func")); 111 ASSERT_NE(Func, nullptr); 112 auto *LocalDecl = cast<VarDecl>(findValueDecl(ASTCtx, "Local")); 113 114 EXPECT_THAT(getReferencedDecls(*Func).Locals, 115 UnorderedElementsAre(LocalDecl)); 116 } 117 118 TEST(ASTOpsTest, LambdaCaptures) { 119 std::string Code = R"cc( 120 void func(int CapturedByRef, int CapturedByValue, int NotCaptured) { 121 int Local; 122 auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) { 123 }; 124 } 125 )cc"; 126 std::unique_ptr<ASTUnit> Unit = 127 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 128 auto &ASTCtx = Unit->getASTContext(); 129 130 ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); 131 132 auto *LambdaCallOp = selectFirst<CXXMethodDecl>( 133 "l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx)); 134 ASSERT_NE(LambdaCallOp, nullptr); 135 auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func")); 136 ASSERT_NE(Func, nullptr); 137 auto *CapturedByRefDecl = Func->getParamDecl(0); 138 ASSERT_NE(CapturedByRefDecl, nullptr); 139 auto *CapturedByValueDecl = Func->getParamDecl(1); 140 ASSERT_NE(CapturedByValueDecl, nullptr); 141 142 EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty()); 143 ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp); 144 EXPECT_THAT(ForLambda.LambdaCapturedParams, 145 UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl)); 146 // Captured locals must be seen in the body for them to appear in 147 // ReferencedDecls. 148 EXPECT_THAT(ForLambda.Locals, IsEmpty()); 149 } 150 151 } // namespace 152