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 mutable std::unique_ptr
<BugType
> BT
;
88 void checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const {
89 if (CallDescription
{{"preventError"}, 0}.matches(Call
)) {
90 C
.addTransition(C
.getState()->set
<ErrorPrevented
>(true));
94 if (CallDescription
{{"allowError"}, 0}.matches(Call
)) {
95 C
.addTransition(C
.getState()->set
<ErrorPrevented
>(false));
99 if (CallDescription
{{"error"}, 0}.matches(Call
)) {
100 if (C
.getState()->get
<ErrorPrevented
>())
102 const ExplodedNode
*N
= C
.generateErrorNode();
106 BT
.reset(new BugType(this->getCheckerName(), "error()",
107 categories::SecurityError
));
109 std::make_unique
<PathSensitiveBugReport
>(*BT
, "error() called", N
);
110 R
->template addVisitor
<Visitor
>();
111 C
.emitReport(std::move(R
));
120 //===----------------------------------------------------------------------===//
121 // Non-thorough analysis: only the state right before and right after the
122 // function call is checked for the difference in trait value.
123 //===----------------------------------------------------------------------===//
129 class NonThoroughErrorNotPreventedFuncVisitor
130 : public ErrorNotPreventedFuncVisitor
{
133 wasModifiedInFunction(const ExplodedNode
*CallEnterN
,
134 const ExplodedNode
*CallExitEndN
) override
{
135 return CallEnterN
->getState()->get
<ErrorPrevented
>() !=
136 CallExitEndN
->getState()->get
<ErrorPrevented
>();
140 void addNonThoroughStatefulChecker(AnalysisASTConsumer
&AnalysisConsumer
,
141 AnalyzerOptions
&AnOpts
) {
142 AnOpts
.CheckersAndPackages
= {{"test.StatefulChecker", true}};
143 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
145 .addChecker
<StatefulChecker
<NonThoroughErrorNotPreventedFuncVisitor
>>(
146 "test.StatefulChecker", "Description", "");
150 TEST(NoStateChangeFuncVisitor
, NonThoroughFunctionAnalysis
) {
152 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
167 "test.StatefulChecker: Calling 'g' | Returning without prevening "
168 "the error | Returning from 'g' | error() called\n");
172 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
188 "test.StatefulChecker: Calling 'g' | Returning without prevening "
189 "the error | Returning from 'g' | error() called\n");
193 EXPECT_TRUE(runCheckerOnCode
<addNonThoroughStatefulChecker
>(R
"(
207 EXPECT_EQ(Diags
, "");
214 //===----------------------------------------------------------------------===//
215 // Thorough analysis: only the state right before and right after the
216 // function call is checked for the difference in trait value.
217 //===----------------------------------------------------------------------===//
223 class ThoroughErrorNotPreventedFuncVisitor
224 : public ErrorNotPreventedFuncVisitor
{
227 wasModifiedBeforeCallExit(const ExplodedNode
*CurrN
,
228 const ExplodedNode
*CallExitBeginN
) override
{
229 return CurrN
->getState()->get
<ErrorPrevented
>() !=
230 CallExitBeginN
->getState()->get
<ErrorPrevented
>();
234 void addThoroughStatefulChecker(AnalysisASTConsumer
&AnalysisConsumer
,
235 AnalyzerOptions
&AnOpts
) {
236 AnOpts
.CheckersAndPackages
= {{"test.StatefulChecker", true}};
237 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
238 Registry
.addChecker
<StatefulChecker
<ThoroughErrorNotPreventedFuncVisitor
>>(
239 "test.StatefulChecker", "Description", "");
243 TEST(NoStateChangeFuncVisitor
, ThoroughFunctionAnalysis
) {
245 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
260 "test.StatefulChecker: Calling 'g' | Returning without prevening "
261 "the error | Returning from 'g' | error() called\n");
265 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
280 EXPECT_EQ(Diags
, "test.StatefulChecker: error() called\n");
284 EXPECT_TRUE(runCheckerOnCode
<addThoroughStatefulChecker
>(R
"(
298 EXPECT_EQ(Diags
, "");