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