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