//===- unittests/Analysis/FlowSensitive/ASTOpsTest.cpp --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Analysis/FlowSensitive/ASTOps.h" #include "TestingSupport.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/LLVM.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Tooling/Tooling.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include namespace { using namespace clang; using namespace dataflow; using ast_matchers::cxxMethodDecl; using ast_matchers::cxxRecordDecl; using ast_matchers::hasName; using ast_matchers::hasType; using ast_matchers::initListExpr; using ast_matchers::match; using ast_matchers::selectFirst; using test::findValueDecl; using testing::IsEmpty; using testing::UnorderedElementsAre; TEST(ASTOpsTest, RecordInitListHelperOnEmptyUnionInitList) { // This is a regression test: The `RecordInitListHelper` used to assert-fail // when called for the `InitListExpr` of an empty union. std::string Code = R"cc( struct S { S() : UField{} {}; union U {} UField; }; )cc"; std::unique_ptr Unit = tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); auto &ASTCtx = Unit->getASTContext(); ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); auto *InitList = selectFirst( "init", match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), ASTCtx)); ASSERT_NE(InitList, nullptr); RecordInitListHelper Helper(InitList); EXPECT_THAT(Helper.base_inits(), IsEmpty()); EXPECT_THAT(Helper.field_inits(), IsEmpty()); } TEST(ASTOpsTest, ReferencedDeclsOnUnionInitList) { // This is a regression test: `getReferencedDecls()` used to return a null // `FieldDecl` in this case (in addition to the correct non-null `FieldDecl`) // because `getInitializedFieldInUnion()` returns null for the syntactic form // of the `InitListExpr`. std::string Code = R"cc( struct S { S() : UField{0} {}; union U { int I; } UField; }; )cc"; std::unique_ptr Unit = tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); auto &ASTCtx = Unit->getASTContext(); ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); auto *InitList = selectFirst( "init", match(initListExpr(hasType(cxxRecordDecl(hasName("U")))).bind("init"), ASTCtx)); ASSERT_NE(InitList, nullptr); auto *IDecl = cast(findValueDecl(ASTCtx, "I")); EXPECT_THAT(getReferencedDecls(*InitList).Fields, UnorderedElementsAre(IDecl)); } TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) { std::string Code = R"cc( void func(int Param) { static int Static = 0; int Local = Param; Local = Static; } )cc"; std::unique_ptr Unit = tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); auto &ASTCtx = Unit->getASTContext(); ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); auto *Func = cast(findValueDecl(ASTCtx, "func")); ASSERT_NE(Func, nullptr); auto *LocalDecl = cast(findValueDecl(ASTCtx, "Local")); EXPECT_THAT(getReferencedDecls(*Func).Locals, UnorderedElementsAre(LocalDecl)); } TEST(ASTOpsTest, LambdaCaptures) { std::string Code = R"cc( void func(int CapturedByRef, int CapturedByValue, int NotCaptured) { int Local; auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) { }; } )cc"; std::unique_ptr Unit = tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); auto &ASTCtx = Unit->getASTContext(); ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U); auto *LambdaCallOp = selectFirst( "l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx)); ASSERT_NE(LambdaCallOp, nullptr); auto *Func = cast(findValueDecl(ASTCtx, "func")); ASSERT_NE(Func, nullptr); auto *CapturedByRefDecl = Func->getParamDecl(0); ASSERT_NE(CapturedByRefDecl, nullptr); auto *CapturedByValueDecl = Func->getParamDecl(1); ASSERT_NE(CapturedByValueDecl, nullptr); EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty()); ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp); EXPECT_THAT(ForLambda.LambdaCapturedParams, UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl)); // Captured locals must be seen in the body for them to appear in // ReferencedDecls. EXPECT_THAT(ForLambda.Locals, IsEmpty()); } } // namespace