//===- 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(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> { \ 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 { 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", "MockDescription"); }); } void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { AnOpts.CheckersAndPackages = {{"ExprEngineVisitPostChecker", true}}; AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { Registry.addChecker( "ExprEngineVisitPostChecker", "MockDescription"); }); } void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}}; AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { Registry.addChecker("MemAccessChecker", "MockDescription"); }); } TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) { std::string Diags; EXPECT_TRUE(runCheckerOnCode(R"( void top() { asm(""); } )", Diags)); EXPECT_EQ(Diags, "ExprEngineVisitPreChecker: checkPreStmt\n"); } TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) { std::string Diags; EXPECT_TRUE(runCheckerOnCode(R"( void top() { asm(""); } )", Diags)); EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt\n"); } TEST(ExprEngineVisitTest, checkLocationAndBind) { std::string Diags; EXPECT_TRUE(runCheckerOnCode(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