aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp
blob: 12be2289c3174896cec853648bada3e1936a2d15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//===- ExprEngineVisitTest.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 "CheckerRegistration.h"
#include "clang/AST/Stmt.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/Support/Casting.h"
#include "gtest/gtest.h"

using namespace clang;
using namespace ento;

namespace {

void emitErrorReport(CheckerContext &C, const BugType &Bug,
                     const std::string &Desc) {
  if (ExplodedNode *Node = C.generateNonFatalErrorNode(C.getState())) {
    auto Report = std::make_unique<PathSensitiveBugReport>(Bug, Desc, Node);
    C.emitReport(std::move(Report));
  }
}

#define CREATE_EXPR_ENGINE_CHECKER(CHECKER_NAME, CALLBACK, STMT_TYPE,          \
                                   BUG_NAME)                                   \
  class CHECKER_NAME : public Checker<check::CALLBACK<STMT_TYPE>> {            \
  public:                                                                      \
    void check##CALLBACK(const STMT_TYPE *ASM, CheckerContext &C) const {      \
      emitErrorReport(C, Bug, "check" #CALLBACK "<" #STMT_TYPE ">");           \
    }                                                                          \
                                                                               \
  private:                                                                     \
    const BugType Bug{this, BUG_NAME};                                         \
  };

CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPreChecker, PreStmt, GCCAsmStmt,
                           "GCCAsmStmtBug")
CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPostChecker, PostStmt, GCCAsmStmt,
                           "GCCAsmStmtBug")

class MemAccessChecker : public Checker<check::Location, check::Bind> {
public:
  void checkLocation(const SVal &Loc, bool IsLoad, const Stmt *S,
                     CheckerContext &C) const {
    emitErrorReport(C, Bug,
                    "checkLocation: Loc = " + dumpToString(Loc) +
                        ", Stmt = " + S->getStmtClassName());
  }

  void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
    emitErrorReport(C, Bug,
                    "checkBind: Loc = " + dumpToString(Loc) +
                        ", Val = " + dumpToString(Val) +
                        ", Stmt = " + S->getStmtClassName());
  }

private:
  const BugType Bug{this, "MemAccess"};

  std::string dumpToString(SVal V) const {
    std::string StrBuf;
    llvm::raw_string_ostream StrStream{StrBuf};
    V.dumpToStream(StrStream);
    return StrBuf;
  }
};

void addExprEngineVisitPreChecker(AnalysisASTConsumer &AnalysisConsumer,
                                  AnalyzerOptions &AnOpts) {
  AnOpts.CheckersAndPackages = {{"ExprEngineVisitPreChecker", true}};
  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
    Registry.addChecker<ExprEngineVisitPreChecker>("ExprEngineVisitPreChecker",
                                                   "MockDescription");
  });
}

void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer,
                                   AnalyzerOptions &AnOpts) {
  AnOpts.CheckersAndPackages = {{"ExprEngineVisitPostChecker", true}};
  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
    Registry.addChecker<ExprEngineVisitPostChecker>(
        "ExprEngineVisitPostChecker", "MockDescription");
  });
}

void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer,
                         AnalyzerOptions &AnOpts) {
  AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}};
  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
    Registry.addChecker<MemAccessChecker>("MemAccessChecker",
                                          "MockDescription");
  });
}

TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) {
  std::string Diags;
  EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPreChecker>(R"(
    void top() {
      asm("");
    }
  )",
                                                             Diags));
  EXPECT_EQ(Diags, "ExprEngineVisitPreChecker: checkPreStmt<GCCAsmStmt>\n");
}

TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) {
  std::string Diags;
  EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPostChecker>(R"(
    void top() {
      asm("");
    }
  )",
                                                              Diags));
  EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt<GCCAsmStmt>\n");
}

TEST(ExprEngineVisitTest, checkLocationAndBind) {
  std::string Diags;
  EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"(
    class MyClass{
    public:
      int Value;
    };
    extern MyClass MyClassWrite, MyClassRead; 
    void top() {
      MyClassWrite = MyClassRead;
    }
  )",
                                                    Diags));

  std::string LocMsg = "checkLocation: Loc = lazyCompoundVal{0x0,MyClassRead}, "
                       "Stmt = ImplicitCastExpr";
  std::string BindMsg =
      "checkBind: Loc = &MyClassWrite, Val = lazyCompoundVal{0x0,MyClassRead}, "
      "Stmt = CXXOperatorCallExpr";
  std::size_t LocPos = Diags.find(LocMsg);
  std::size_t BindPos = Diags.find(BindMsg);
  EXPECT_NE(LocPos, std::string::npos);
  EXPECT_NE(BindPos, std::string::npos);
  // Check order: first checkLocation is called, then checkBind.
  // In the diagnosis, however, the messages appear in reverse order.
  EXPECT_TRUE(LocPos > BindPos);
}

} // namespace