Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / StaticAnalyzer / NoStateChangeFuncVisitorTest.cpp
blob69e29b47f925729bec0331f915cc3b4ea842e2b9
1 //===- unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.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-exception
6 //
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"
27 #include <memory>
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)
44 namespace clang {
45 namespace ento {
46 namespace {
48 class ErrorNotPreventedFuncVisitor : public NoStateChangeFuncVisitor {
49 public:
50 ErrorNotPreventedFuncVisitor()
51 : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {}
53 virtual PathDiagnosticPieceRef
54 maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
55 const ObjCMethodCall &Call,
56 const ExplodedNode *N) override {
57 return nullptr;
60 virtual PathDiagnosticPieceRef
61 maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
62 const CXXConstructorCall &Call,
63 const ExplodedNode *N) override {
64 return nullptr;
67 virtual PathDiagnosticPieceRef
68 maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
69 const ExplodedNode *N) override {
70 PathDiagnosticLocation L = PathDiagnosticLocation::create(
71 N->getLocation(),
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 {
78 static int Tag = 0;
79 ID.AddPointer(&Tag);
83 template <class Visitor>
84 class StatefulChecker : public Checker<check::PreCall> {
85 mutable std::unique_ptr<BugType> BT;
87 public:
88 void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
89 if (CallDescription{{"preventError"}, 0}.matches(Call)) {
90 C.addTransition(C.getState()->set<ErrorPrevented>(true));
91 return;
94 if (CallDescription{{"allowError"}, 0}.matches(Call)) {
95 C.addTransition(C.getState()->set<ErrorPrevented>(false));
96 return;
99 if (CallDescription{{"error"}, 0}.matches(Call)) {
100 if (C.getState()->get<ErrorPrevented>())
101 return;
102 const ExplodedNode *N = C.generateErrorNode();
103 if (!N)
104 return;
105 if (!BT)
106 BT.reset(new BugType(this->getCheckerName(), "error()",
107 categories::SecurityError));
108 auto R =
109 std::make_unique<PathSensitiveBugReport>(*BT, "error() called", N);
110 R->template addVisitor<Visitor>();
111 C.emitReport(std::move(R));
116 } // namespace
117 } // namespace ento
118 } // namespace clang
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 //===----------------------------------------------------------------------===//
125 namespace clang {
126 namespace ento {
127 namespace {
129 class NonThoroughErrorNotPreventedFuncVisitor
130 : public ErrorNotPreventedFuncVisitor {
131 public:
132 virtual bool
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) {
144 Registry
145 .addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>(
146 "test.StatefulChecker", "Description", "");
150 TEST(NoStateChangeFuncVisitor, NonThoroughFunctionAnalysis) {
151 std::string Diags;
152 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
153 void error();
154 void preventError();
155 void allowError();
157 void g() {
158 //preventError();
161 void f() {
162 g();
163 error();
165 )", Diags));
166 EXPECT_EQ(Diags,
167 "test.StatefulChecker: Calling 'g' | Returning without prevening "
168 "the error | Returning from 'g' | error() called\n");
170 Diags.clear();
172 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
173 void error();
174 void preventError();
175 void allowError();
177 void g() {
178 preventError();
179 allowError();
182 void f() {
183 g();
184 error();
186 )", Diags));
187 EXPECT_EQ(Diags,
188 "test.StatefulChecker: Calling 'g' | Returning without prevening "
189 "the error | Returning from 'g' | error() called\n");
191 Diags.clear();
193 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
194 void error();
195 void preventError();
196 void allowError();
198 void g() {
199 preventError();
202 void f() {
203 g();
204 error();
206 )", Diags));
207 EXPECT_EQ(Diags, "");
210 } // namespace
211 } // namespace ento
212 } // namespace clang
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 //===----------------------------------------------------------------------===//
219 namespace clang {
220 namespace ento {
221 namespace {
223 class ThoroughErrorNotPreventedFuncVisitor
224 : public ErrorNotPreventedFuncVisitor {
225 public:
226 virtual bool
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) {
244 std::string Diags;
245 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
246 void error();
247 void preventError();
248 void allowError();
250 void g() {
251 //preventError();
254 void f() {
255 g();
256 error();
258 )", Diags));
259 EXPECT_EQ(Diags,
260 "test.StatefulChecker: Calling 'g' | Returning without prevening "
261 "the error | Returning from 'g' | error() called\n");
263 Diags.clear();
265 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
266 void error();
267 void preventError();
268 void allowError();
270 void g() {
271 preventError();
272 allowError();
275 void f() {
276 g();
277 error();
279 )", Diags));
280 EXPECT_EQ(Diags, "test.StatefulChecker: error() called\n");
282 Diags.clear();
284 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
285 void error();
286 void preventError();
287 void allowError();
289 void g() {
290 preventError();
293 void f() {
294 g();
295 error();
297 )", Diags));
298 EXPECT_EQ(Diags, "");
301 } // namespace
302 } // namespace ento
303 } // namespace clang