1 //===- unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp ----------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "CheckerRegistration.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
12 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
13 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
23 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
24 #include "llvm/Support/ErrorHandling.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include "gtest/gtest.h"
29 //===----------------------------------------------------------------------===//
30 // Base classes for testing NoStateChangeFuncVisitor.
32 // Testing is done by observing a very simple trait change from one node to
33 // another -- the checker sets the ErrorPrevented trait to true if
34 // 'preventError()' is called in the source code, and sets it to false if
35 // 'allowError()' is called. If this trait is false when 'error()' is called,
36 // a warning is emitted.
38 // The checker then registers a simple NoStateChangeFuncVisitor to add notes to
39 // inlined functions that could have, but neglected to prevent the error.
40 //===----------------------------------------------------------------------===//
42 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrorPrevented
, bool)
48 class ErrorNotPreventedFuncVisitor
: public NoStateChangeFuncVisitor
{
50 ErrorNotPreventedFuncVisitor()
51 : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough
) {}
53 virtual PathDiagnosticPieceRef
54 maybeEmitNoteForObjCSelf(PathSensitiveBugReport
&R
,
55 const ObjCMethodCall
&Call
,
56 const ExplodedNode
*N
) override
{
60 virtual PathDiagnosticPieceRef
61 maybeEmitNoteForCXXThis(PathSensitiveBugReport
&R
,
62 const CXXConstructorCall
&Call
,
63 const ExplodedNode
*N
) override
{
67 virtual PathDiagnosticPieceRef
68 maybeEmitNoteForParameters(PathSensitiveBugReport
&R
, const CallEvent
&Call
,
69 const ExplodedNode
*N
) override
{
70 PathDiagnosticLocation L
= PathDiagnosticLocation::create(
72 N
->getState()->getStateManager().getContext().getSourceManager());
73 return std::make_shared
<PathDiagnosticEventPiece
>(
74 L
, "Returning without prevening the error");
77 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
83 template <class Visitor
>
84 class StatefulChecker
: public Checker
<check::PreCall
> {
85 const BugType BT
{this, "error()", categories::SecurityError
};
88 void checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const {
89 if (CallDescription
{CDM::SimpleFunc
, {"preventError"}, 0}.matches(Call
)) {
90 C
.addTransition(C
.getState()->set
<ErrorPrevented
>(true));
94 if (CallDescription
{CDM::SimpleFunc
, {"allowError"}, 0}.matches(Call
)) {
95 C
.addTransition(C
.getState()->set
<ErrorPrevented
>(false));
99 if (CallDescription
{CDM::SimpleFunc
, {"error"}, 0}.matches(Call
)) {
100 if (C
.getState()->get
<ErrorPrevented
>())
102 const ExplodedNode
*N
= C
.generateErrorNode();
106 std::make_unique
<PathSensitiveBugReport
>(BT
, "error() called", N
);
107 R
->template addVisitor
<Visitor
>();
108 C
.emitReport(std::move(R
));
117 //===----------------------------------------------------------------------===//
118 // Non-thorough analysis: only the state right before and right after the
119 // function call is checked for the difference in trait value.
120 //===----------------------------------------------------------------------===//
126 class NonThoroughErrorNotPreventedFuncVisitor
127 : public ErrorNotPreventedFuncVisitor
{
130 wasModifiedInFunction(const ExplodedNode
*CallEnterN
,
131 const ExplodedNode
*CallExitEndN
) override
{
132 return CallEnterN
->getState()->get
<ErrorPrevented
>() !=
133 CallExitEndN
->getState()->get
<ErrorPrevented
>();
137 void addNonThoroughStatefulChecker(AnalysisASTConsumer
&AnalysisConsumer
,
138 AnalyzerOptions
&AnOpts
) {
139 AnOpts
.CheckersAndPackages
= {{"test.StatefulChecker", true}};
140 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
142 .addChecker
<StatefulChecker
<NonThoroughErrorNotPreventedFuncVisitor
>>(
143 "test.StatefulChecker", "Description", "");
147 TEST(NoStateChangeFuncVisitor
, NonThoroughFunctionAnalysis
) {
149 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
164 "test.StatefulChecker: Calling 'g' | Returning without prevening "
165 "the error | Returning from 'g' | error() called\n");
169 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
185 "test.StatefulChecker: Calling 'g' | Returning without prevening "
186 "the error | Returning from 'g' | error() called\n");
190 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
204 EXPECT_EQ(Diags
, "");
211 //===----------------------------------------------------------------------===//
212 // Thorough analysis: only the state right before and right after the
213 // function call is checked for the difference in trait value.
214 //===----------------------------------------------------------------------===//
220 class ThoroughErrorNotPreventedFuncVisitor
221 : public ErrorNotPreventedFuncVisitor
{
224 wasModifiedBeforeCallExit(const ExplodedNode
*CurrN
,
225 const ExplodedNode
*CallExitBeginN
) override
{
226 return CurrN
->getState()->get
<ErrorPrevented
>() !=
227 CallExitBeginN
->getState()->get
<ErrorPrevented
>();
231 void addThoroughStatefulChecker(AnalysisASTConsumer
&AnalysisConsumer
,
232 AnalyzerOptions
&AnOpts
) {
233 AnOpts
.CheckersAndPackages
= {{"test.StatefulChecker", true}};
234 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
235 Registry
.addChecker
<StatefulChecker
<ThoroughErrorNotPreventedFuncVisitor
>>(
236 "test.StatefulChecker", "Description", "");
240 TEST(NoStateChangeFuncVisitor
, ThoroughFunctionAnalysis
) {
242 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
257 "test.StatefulChecker: Calling 'g' | Returning without prevening "
258 "the error | Returning from 'g' | error() called\n");
262 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
277 EXPECT_EQ(Diags
, "test.StatefulChecker: error() called\n");
281 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
295 EXPECT_EQ(Diags
, "");