1//===- ExprEngineVisitTest.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 "CheckerRegistration.h"
10#include "clang/AST/Stmt.h"
11#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
12#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
13#include "clang/StaticAnalyzer/Core/Checker.h"
14#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
15#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
16#include "llvm/Support/Casting.h"
17#include "gtest/gtest.h"
18
19using namespace clang;
20using namespace ento;
21
22namespace {
23
24void emitErrorReport(CheckerContext &C, const BugType &Bug,
25 const std::string &Desc) {
26 if (ExplodedNode *Node = C.generateNonFatalErrorNode(State: C.getState())) {
27 auto Report = std::make_unique<PathSensitiveBugReport>(args: Bug, args: Desc, args&: Node);
28 C.emitReport(R: std::move(Report));
29 }
30}
31
32#define CREATE_EXPR_ENGINE_CHECKER(CHECKER_NAME, CALLBACK, STMT_TYPE, \
33 BUG_NAME) \
34 class CHECKER_NAME : public Checker<check::CALLBACK<STMT_TYPE>> { \
35 public: \
36 void check##CALLBACK(const STMT_TYPE *ASM, CheckerContext &C) const { \
37 emitErrorReport(C, Bug, "check" #CALLBACK "<" #STMT_TYPE ">"); \
38 } \
39 \
40 private: \
41 const BugType Bug{this, BUG_NAME}; \
42 };
43
44CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPreChecker, PreStmt, GCCAsmStmt,
45 "GCCAsmStmtBug")
46CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPostChecker, PostStmt, GCCAsmStmt,
47 "GCCAsmStmtBug")
48
49class MemAccessChecker : public Checker<check::Location, check::Bind> {
50public:
51 void checkLocation(const SVal &Loc, bool IsLoad, const Stmt *S,
52 CheckerContext &C) const {
53 emitErrorReport(C, Bug,
54 Desc: "checkLocation: Loc = " + dumpToString(V: Loc) +
55 ", Stmt = " + S->getStmtClassName());
56 }
57
58 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
59 emitErrorReport(C, Bug,
60 Desc: "checkBind: Loc = " + dumpToString(V: Loc) +
61 ", Val = " + dumpToString(V: Val) +
62 ", Stmt = " + S->getStmtClassName());
63 }
64
65private:
66 const BugType Bug{this, "MemAccess"};
67
68 std::string dumpToString(SVal V) const {
69 std::string StrBuf;
70 llvm::raw_string_ostream StrStream{StrBuf};
71 V.dumpToStream(OS&: StrStream);
72 return StrBuf;
73 }
74};
75
76void addExprEngineVisitPreChecker(AnalysisASTConsumer &AnalysisConsumer,
77 AnalyzerOptions &AnOpts) {
78 AnOpts.CheckersAndPackages = {{"ExprEngineVisitPreChecker", true}};
79 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
80 Registry.addChecker<ExprEngineVisitPreChecker>(FullName: "ExprEngineVisitPreChecker",
81 Desc: "Desc", DocsUri: "DocsURI");
82 });
83}
84
85void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer,
86 AnalyzerOptions &AnOpts) {
87 AnOpts.CheckersAndPackages = {{"ExprEngineVisitPostChecker", true}};
88 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
89 Registry.addChecker<ExprEngineVisitPostChecker>(
90 FullName: "ExprEngineVisitPostChecker", Desc: "Desc", DocsUri: "DocsURI");
91 });
92}
93
94void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer,
95 AnalyzerOptions &AnOpts) {
96 AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}};
97 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
98 Registry.addChecker<MemAccessChecker>(FullName: "MemAccessChecker", Desc: "Desc",
99 DocsUri: "DocsURI");
100 });
101}
102
103TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) {
104 std::string Diags;
105 EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPreChecker>(R"(
106 void top() {
107 asm("");
108 }
109 )",
110 Diags));
111 EXPECT_EQ(Diags, "ExprEngineVisitPreChecker: checkPreStmt<GCCAsmStmt>\n");
112}
113
114TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) {
115 std::string Diags;
116 EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPostChecker>(R"(
117 void top() {
118 asm("");
119 }
120 )",
121 Diags));
122 EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt<GCCAsmStmt>\n");
123}
124
125TEST(ExprEngineVisitTest, checkLocationAndBind) {
126 std::string Diags;
127 EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"(
128 class MyClass{
129 public:
130 int Value;
131 };
132 extern MyClass MyClassWrite, MyClassRead;
133 void top() {
134 MyClassWrite = MyClassRead;
135 }
136 )",
137 Diags));
138
139 std::string LocMsg = "checkLocation: Loc = lazyCompoundVal{0x0,MyClassRead}, "
140 "Stmt = ImplicitCastExpr";
141 std::string BindMsg =
142 "checkBind: Loc = &MyClassWrite, Val = lazyCompoundVal{0x0,MyClassRead}, "
143 "Stmt = CXXOperatorCallExpr";
144 std::size_t LocPos = Diags.find(str: LocMsg);
145 std::size_t BindPos = Diags.find(str: BindMsg);
146 EXPECT_NE(LocPos, std::string::npos);
147 EXPECT_NE(BindPos, std::string::npos);
148 // Check order: first checkLocation is called, then checkBind.
149 // In the diagnosis, however, the messages appear in reverse order.
150 EXPECT_TRUE(LocPos > BindPos);
151}
152
153} // namespace
154

source code of clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp