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