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