Mercurial > hg > CbC > CbC_llvm
view clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp @ 252:1f2b6ac9f198 llvm-original
LLVM16-1
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 18 Aug 2023 09:04:13 +0900 |
parents | c4bab56944e8 |
children |
line wrap: on
line source
//===- unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.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/Frontend/CompilerInstance.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include <memory> //===----------------------------------------------------------------------===// // Base classes for testing NoStateChangeFuncVisitor. // // Testing is done by observing a very simple trait change from one node to // another -- the checker sets the ErrorPrevented trait to true if // 'preventError()' is called in the source code, and sets it to false if // 'allowError()' is called. If this trait is false when 'error()' is called, // a warning is emitted. // // The checker then registers a simple NoStateChangeFuncVisitor to add notes to // inlined functions that could have, but neglected to prevent the error. //===----------------------------------------------------------------------===// REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrorPrevented, bool) namespace clang { namespace ento { namespace { class ErrorNotPreventedFuncVisitor : public NoStateChangeFuncVisitor { public: ErrorNotPreventedFuncVisitor() : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {} virtual PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, const ObjCMethodCall &Call, const ExplodedNode *N) override { return nullptr; } virtual PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, const CXXConstructorCall &Call, const ExplodedNode *N) override { return nullptr; } virtual PathDiagnosticPieceRef maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) override { PathDiagnosticLocation L = PathDiagnosticLocation::create( N->getLocation(), N->getState()->getStateManager().getContext().getSourceManager()); return std::make_shared<PathDiagnosticEventPiece>( L, "Returning without prevening the error"); } void Profile(llvm::FoldingSetNodeID &ID) const override { static int Tag = 0; ID.AddPointer(&Tag); } }; template <class Visitor> class StatefulChecker : public Checker<check::PreCall> { mutable std::unique_ptr<BugType> BT; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (CallDescription{{"preventError"}, 0}.matches(Call)) { C.addTransition(C.getState()->set<ErrorPrevented>(true)); return; } if (CallDescription{{"allowError"}, 0}.matches(Call)) { C.addTransition(C.getState()->set<ErrorPrevented>(false)); return; } if (CallDescription{{"error"}, 0}.matches(Call)) { if (C.getState()->get<ErrorPrevented>()) return; const ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT) BT.reset(new BugType(this->getCheckerName(), "error()", categories::SecurityError)); auto R = std::make_unique<PathSensitiveBugReport>(*BT, "error() called", N); R->template addVisitor<Visitor>(); C.emitReport(std::move(R)); } } }; } // namespace } // namespace ento } // namespace clang //===----------------------------------------------------------------------===// // Non-thorough analysis: only the state right before and right after the // function call is checked for the difference in trait value. //===----------------------------------------------------------------------===// namespace clang { namespace ento { namespace { class NonThoroughErrorNotPreventedFuncVisitor : public ErrorNotPreventedFuncVisitor { public: virtual bool wasModifiedInFunction(const ExplodedNode *CallEnterN, const ExplodedNode *CallExitEndN) override { return CallEnterN->getState()->get<ErrorPrevented>() != CallExitEndN->getState()->get<ErrorPrevented>(); } }; void addNonThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}}; AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { Registry .addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>( "test.StatefulChecker", "Description", ""); }); } TEST(NoStateChangeFuncVisitor, NonThoroughFunctionAnalysis) { std::string Diags; EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { //preventError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, "test.StatefulChecker: Calling 'g' | Returning without prevening " "the error | Returning from 'g' | error() called\n"); Diags.clear(); EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { preventError(); allowError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, "test.StatefulChecker: Calling 'g' | Returning without prevening " "the error | Returning from 'g' | error() called\n"); Diags.clear(); EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { preventError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, ""); } } // namespace } // namespace ento } // namespace clang //===----------------------------------------------------------------------===// // Thorough analysis: only the state right before and right after the // function call is checked for the difference in trait value. //===----------------------------------------------------------------------===// namespace clang { namespace ento { namespace { class ThoroughErrorNotPreventedFuncVisitor : public ErrorNotPreventedFuncVisitor { public: virtual bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) override { return CurrN->getState()->get<ErrorPrevented>() != CallExitBeginN->getState()->get<ErrorPrevented>(); } }; void addThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}}; AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { Registry.addChecker<StatefulChecker<ThoroughErrorNotPreventedFuncVisitor>>( "test.StatefulChecker", "Description", ""); }); } TEST(NoStateChangeFuncVisitor, ThoroughFunctionAnalysis) { std::string Diags; EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { //preventError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, "test.StatefulChecker: Calling 'g' | Returning without prevening " "the error | Returning from 'g' | error() called\n"); Diags.clear(); EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { preventError(); allowError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, "test.StatefulChecker: error() called\n"); Diags.clear(); EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( void error(); void preventError(); void allowError(); void g() { preventError(); } void f() { g(); error(); } )", Diags)); EXPECT_EQ(Diags, ""); } } // namespace } // namespace ento } // namespace clang