| //===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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/CFGMatchSwitch.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/Stmt.h" |
| #include "clang/Analysis/CFG.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "gtest/gtest.h" |
| |
| using namespace clang; |
| using namespace dataflow; |
| using namespace ast_matchers; |
| |
| namespace { |
| // State for tracking the number of matches on each kind of CFGElement by the |
| // CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer. |
| struct CFGElementMatches { |
| unsigned StmtMatches = 0; |
| unsigned InitializerMatches = 0; |
| }; |
| |
| // Returns a match switch that counts the number of local variables |
| // (singly-declared) and fields initialized to the integer literal 42. |
| auto buildCFGMatchSwitch() { |
| return CFGMatchSwitchBuilder<CFGElementMatches>() |
| .CaseOfCFGStmt<DeclStmt>( |
| declStmt(hasSingleDecl( |
| varDecl(hasInitializer(integerLiteral(equals(42)))))), |
| [](const DeclStmt *, const MatchFinder::MatchResult &, |
| CFGElementMatches &Counter) { Counter.StmtMatches++; }) |
| .CaseOfCFGInit<CXXCtorInitializer>( |
| cxxCtorInitializer(withInitializer(integerLiteral(equals(42)))), |
| [](const CXXCtorInitializer *, const MatchFinder::MatchResult &, |
| CFGElementMatches &Counter) { Counter.InitializerMatches++; }) |
| .Build(); |
| } |
| |
| // Runs the match switch `MS` on the control flow graph generated from `Code`, |
| // tracking information in state `S`. For simplicity, this test utility is |
| // restricted to CFGs with a single control flow block (excluding entry and |
| // exit blocks) - generated by `Code` with sequential flow (i.e. no branching). |
| // |
| // Requirements: |
| // |
| // `Code` must contain a function named `f`, the body of this function will be |
| // used to generate the CFG. |
| template <typename State> |
| void applySwitchToCode(CFGMatchSwitch<State> &MS, State &S, |
| llvm::StringRef Code) { |
| auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"}); |
| auto &Ctx = Unit->getASTContext(); |
| const auto *F = selectFirst<FunctionDecl>( |
| "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx)); |
| |
| CFG::BuildOptions BO; |
| BO.AddInitializers = true; |
| |
| auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO); |
| auto CFGBlock = *CFG->getEntry().succ_begin(); |
| for (auto &Elt : CFGBlock->Elements) { |
| MS(Elt, Ctx, S); |
| } |
| } |
| |
| TEST(CFGMatchSwitchTest, NoInitializationTo42) { |
| CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch(); |
| CFGElementMatches Counter; |
| applySwitchToCode(Switch, Counter, R"( |
| void f() { |
| 42; |
| } |
| )"); |
| EXPECT_EQ(Counter.StmtMatches, 0u); |
| EXPECT_EQ(Counter.InitializerMatches, 0u); |
| } |
| |
| TEST(CFGMatchSwitchTest, SingleLocalVarInitializationTo42) { |
| CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch(); |
| CFGElementMatches Counter; |
| applySwitchToCode(Switch, Counter, R"( |
| void f() { |
| int i = 42; |
| } |
| )"); |
| EXPECT_EQ(Counter.StmtMatches, 1u); |
| EXPECT_EQ(Counter.InitializerMatches, 0u); |
| } |
| |
| TEST(CFGMatchSwitchTest, SingleFieldInitializationTo42) { |
| CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch(); |
| CFGElementMatches Counter; |
| applySwitchToCode(Switch, Counter, R"( |
| struct f { |
| int i; |
| f(): i(42) {} |
| }; |
| )"); |
| EXPECT_EQ(Counter.StmtMatches, 0u); |
| EXPECT_EQ(Counter.InitializerMatches, 1u); |
| } |
| |
| TEST(CFGMatchSwitchTest, LocalVarAndFieldInitializationTo42) { |
| CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch(); |
| CFGElementMatches Counter; |
| applySwitchToCode(Switch, Counter, R"( |
| struct f { |
| int i; |
| f(): i(42) { |
| int j = 42; |
| } |
| }; |
| )"); |
| EXPECT_EQ(Counter.StmtMatches, 1u); |
| EXPECT_EQ(Counter.InitializerMatches, 1u); |
| } |
| } // namespace |