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