xref: /llvm-project/clang/unittests/StaticAnalyzer/BugReportInterestingnessTest.cpp (revision 58bad2862cf136f9483eb005bbfa6915d459b46d)
1 //===----------------------------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 
9 #include "Reusables.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
12 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
13 #include "clang/StaticAnalyzer/Core/Checker.h"
14 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
19 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gtest/gtest.h"
22 
23 using namespace clang;
24 using namespace ento;
25 using namespace llvm;
26 
27 namespace {
28 
29 class InterestingnessTestChecker : public Checker<check::PreCall> {
30   BugType BT_TestBug;
31 
32   using HandlerFn = std::function<void(const InterestingnessTestChecker *,
33                                        const CallEvent &, CheckerContext &)>;
34 
35   CallDescriptionMap<HandlerFn> Handlers = {
36       {{CDM::SimpleFunc, {"setInteresting"}, 1},
37        &InterestingnessTestChecker::handleInteresting},
38       {{CDM::SimpleFunc, {"setNotInteresting"}, 1},
39        &InterestingnessTestChecker::handleNotInteresting},
40       {{CDM::SimpleFunc, {"check"}, 1},
41        &InterestingnessTestChecker::handleCheck},
42       {{CDM::SimpleFunc, {"bug"}, 1}, &InterestingnessTestChecker::handleBug},
43   };
44 
45   void handleInteresting(const CallEvent &Call, CheckerContext &C) const;
46   void handleNotInteresting(const CallEvent &Call, CheckerContext &C) const;
47   void handleCheck(const CallEvent &Call, CheckerContext &C) const;
48   void handleBug(const CallEvent &Call, CheckerContext &C) const;
49 
50 public:
InterestingnessTestChecker()51   InterestingnessTestChecker()
52       : BT_TestBug(this, "InterestingnessTestBug", "Test") {}
53 
checkPreCall(const CallEvent & Call,CheckerContext & C) const54   void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
55     const HandlerFn *Handler = Handlers.lookup(Call);
56     if (!Handler)
57       return;
58 
59     (*Handler)(this, Call, C);
60   }
61 };
62 
63 } // namespace
64 
handleInteresting(const CallEvent & Call,CheckerContext & C) const65 void InterestingnessTestChecker::handleInteresting(const CallEvent &Call,
66                                                    CheckerContext &C) const {
67   SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
68   assert(Sym);
69   C.addTransition(nullptr, C.getNoteTag([Sym](PathSensitiveBugReport &BR) {
70     BR.markInteresting(Sym);
71     return "";
72   }));
73 }
74 
handleNotInteresting(const CallEvent & Call,CheckerContext & C) const75 void InterestingnessTestChecker::handleNotInteresting(const CallEvent &Call,
76                                                       CheckerContext &C) const {
77   SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
78   assert(Sym);
79   C.addTransition(nullptr, C.getNoteTag([Sym](PathSensitiveBugReport &BR) {
80     BR.markNotInteresting(Sym);
81     return "";
82   }));
83 }
84 
handleCheck(const CallEvent & Call,CheckerContext & C) const85 void InterestingnessTestChecker::handleCheck(const CallEvent &Call,
86                                              CheckerContext &C) const {
87   SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
88   assert(Sym);
89   C.addTransition(nullptr, C.getNoteTag([Sym](PathSensitiveBugReport &BR) {
90     if (BR.isInteresting(Sym))
91       return "Interesting";
92     else
93       return "NotInteresting";
94   }));
95 }
96 
handleBug(const CallEvent & Call,CheckerContext & C) const97 void InterestingnessTestChecker::handleBug(const CallEvent &Call,
98                                            CheckerContext &C) const {
99   ExplodedNode *N = C.generateErrorNode();
100   C.emitReport(
101       std::make_unique<PathSensitiveBugReport>(BT_TestBug, "test bug", N));
102 }
103 
104 namespace {
105 
106 class TestAction : public ASTFrontendAction {
107   ExpectedDiagsTy ExpectedDiags;
108 
109 public:
TestAction(ExpectedDiagsTy && ExpectedDiags)110   TestAction(ExpectedDiagsTy &&ExpectedDiags)
111       : ExpectedDiags(std::move(ExpectedDiags)) {}
112 
CreateASTConsumer(CompilerInstance & Compiler,StringRef File)113   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
114                                                  StringRef File) override {
115     std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =
116         CreateAnalysisConsumer(Compiler);
117     AnalysisConsumer->AddDiagnosticConsumer(new VerifyPathDiagnosticConsumer(
118         std::move(ExpectedDiags), Compiler.getSourceManager()));
119     AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
120       Registry.addChecker<InterestingnessTestChecker>("test.Interestingness",
121                                                       "Description", "");
122     });
123     Compiler.getAnalyzerOpts().CheckersAndPackages = {
124         {"test.Interestingness", true}};
125     return std::move(AnalysisConsumer);
126   }
127 };
128 
129 } // namespace
130 
TEST(BugReportInterestingness,Symbols)131 TEST(BugReportInterestingness, Symbols) {
132   EXPECT_TRUE(tooling::runToolOnCode(
133       std::make_unique<TestAction>(ExpectedDiagsTy{
134           {{15, 7},
135            "test bug",
136            "test bug",
137            "test.Interestingness",
138            "InterestingnessTestBug",
139            "Test",
140            {
141                {{8, 7}, "Interesting", {{{8, 7}, {8, 14}}}},
142                {{10, 7}, "NotInteresting", {{{10, 7}, {10, 14}}}},
143                {{12, 7}, "Interesting", {{{12, 7}, {12, 14}}}},
144                {{14, 7}, "NotInteresting", {{{14, 7}, {14, 14}}}},
145                {{15, 7}, "test bug", {{{15, 7}, {15, 12}}}},
146            }}}),
147       R"(
148     void setInteresting(int);
149     void setNotInteresting(int);
150     void check(int);
151     void bug(int);
152 
153     void f(int A) {
154       check(A);
155       setInteresting(A);
156       check(A);
157       setNotInteresting(A);
158       check(A);
159       setInteresting(A);
160       check(A);
161       bug(A);
162     }
163   )",
164       "input.cpp"));
165 }
166