172 lines · cpp
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-exception6//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(C.getState())) {27 auto Report = std::make_unique<PathSensitiveBugReport>(Bug, Desc, Node);28 C.emitReport(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 "checkLocation: Loc = " + dumpToString(Loc) +55 ", Stmt = " + S->getStmtClassName());56 }57 58 void checkBind(SVal Loc, SVal Val, const Stmt *S, bool AtDeclInit,59 CheckerContext &C) const {60 emitErrorReport(C, Bug,61 "checkBind: Loc = " + dumpToString(Loc) +62 ", Val = " + dumpToString(Val) +63 ", Stmt = " + S->getStmtClassName() +64 ", AtDeclInit = " + (AtDeclInit ? "true" : "false"));65 }66 67private:68 const BugType Bug{this, "MemAccess"};69 70 std::string dumpToString(SVal V) const {71 std::string StrBuf;72 llvm::raw_string_ostream StrStream{StrBuf};73 V.dumpToStream(StrStream);74 return StrBuf;75 }76};77 78void addExprEngineVisitPreChecker(AnalysisASTConsumer &AnalysisConsumer,79 AnalyzerOptions &AnOpts) {80 AnOpts.CheckersAndPackages = {{"ExprEngineVisitPreChecker", true}};81 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {82 Registry.addChecker<ExprEngineVisitPreChecker>("ExprEngineVisitPreChecker",83 "MockDescription");84 });85}86 87void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer,88 AnalyzerOptions &AnOpts) {89 AnOpts.CheckersAndPackages = {{"ExprEngineVisitPostChecker", true}};90 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {91 Registry.addChecker<ExprEngineVisitPostChecker>(92 "ExprEngineVisitPostChecker", "MockDescription");93 });94}95 96void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer,97 AnalyzerOptions &AnOpts) {98 AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}};99 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {100 Registry.addChecker<MemAccessChecker>("MemAccessChecker",101 "MockDescription");102 });103}104 105TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) {106 std::string Diags;107 EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPreChecker>(R"(108 void top() {109 asm("");110 }111 )",112 Diags));113 EXPECT_EQ(Diags, "ExprEngineVisitPreChecker: checkPreStmt<GCCAsmStmt>\n");114}115 116TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) {117 std::string Diags;118 EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPostChecker>(R"(119 void top() {120 asm("");121 }122 )",123 Diags));124 EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt<GCCAsmStmt>\n");125}126 127TEST(ExprEngineVisitTest, checkLocationAndBind) {128 std::string Diags;129 EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"(130 class MyClass{131 public:132 int Value;133 };134 extern MyClass MyClassWrite, MyClassRead; 135 void top() {136 MyClassWrite = MyClassRead;137 }138 )",139 Diags));140 141 std::string LocMsg = "checkLocation: Loc = lazyCompoundVal{0x0,MyClassRead}, "142 "Stmt = ImplicitCastExpr";143 std::string BindMsg =144 "checkBind: Loc = &MyClassWrite, Val = lazyCompoundVal{0x0,MyClassRead}, "145 "Stmt = CXXOperatorCallExpr, AtDeclInit = false";146 std::size_t LocPos = Diags.find(LocMsg);147 std::size_t BindPos = Diags.find(BindMsg);148 EXPECT_NE(LocPos, std::string::npos);149 EXPECT_NE(BindPos, std::string::npos);150 // Check order: first checkLocation is called, then checkBind.151 // In the diagnosis, however, the messages appear in reverse order.152 EXPECT_TRUE(LocPos > BindPos);153}154 155TEST(ExprEngineVisitTest, checkLocationAndBindInitialization) {156 std::string Diags;157 EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"(158 class MyClass{159 public:160 int Value;161 };162 void top(MyClass param) {163 MyClass MyClassWrite = param;164 }165 )",166 Diags));167 168 EXPECT_TRUE(StringRef(Diags).contains("AtDeclInit = true"));169}170 171} // namespace172