1 | //===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===// |
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 | // This checker prints callbacks that are called during analysis. |
10 | // This is required to ensure that callbacks are fired in order |
11 | // and do not duplicate or get lost. |
12 | // Feel free to extend this checker with any callback you need to check. |
13 | // |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #include "clang/AST/ExprCXX.h" |
17 | #include "clang/Analysis/CFGStmtMap.h" |
18 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "llvm/Support/ErrorHandling.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | namespace { |
29 | |
30 | class AnalysisOrderChecker |
31 | : public Checker< |
32 | check::PreStmt<CastExpr>, check::PostStmt<CastExpr>, |
33 | check::PreStmt<ArraySubscriptExpr>, |
34 | check::PostStmt<ArraySubscriptExpr>, check::PreStmt<CXXNewExpr>, |
35 | check::PostStmt<CXXNewExpr>, check::PreStmt<CXXDeleteExpr>, |
36 | check::PostStmt<CXXDeleteExpr>, check::PreStmt<CXXConstructExpr>, |
37 | check::PostStmt<CXXConstructExpr>, check::PreStmt<OffsetOfExpr>, |
38 | check::PostStmt<OffsetOfExpr>, check::PreCall, check::PostCall, |
39 | check::EndFunction, check::EndAnalysis, check::NewAllocator, |
40 | check::Bind, check::PointerEscape, check::RegionChanges, |
41 | check::LiveSymbols, eval::Call> { |
42 | |
43 | bool isCallbackEnabled(const AnalyzerOptions &Opts, |
44 | StringRef CallbackName) const { |
45 | return Opts.getCheckerBooleanOption(C: this, OptionName: "*" ) || |
46 | Opts.getCheckerBooleanOption(C: this, OptionName: CallbackName); |
47 | } |
48 | |
49 | bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { |
50 | AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions(); |
51 | return isCallbackEnabled(Opts, CallbackName); |
52 | } |
53 | |
54 | bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const { |
55 | AnalyzerOptions &Opts = State->getStateManager().getOwningEngine() |
56 | .getAnalysisManager().getAnalyzerOptions(); |
57 | return isCallbackEnabled(Opts, CallbackName); |
58 | } |
59 | |
60 | public: |
61 | void checkPreStmt(const CastExpr *CE, CheckerContext &C) const { |
62 | if (isCallbackEnabled(C, CallbackName: "PreStmtCastExpr" )) |
63 | llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName() |
64 | << ")\n" ; |
65 | } |
66 | |
67 | void checkPostStmt(const CastExpr *CE, CheckerContext &C) const { |
68 | if (isCallbackEnabled(C, CallbackName: "PostStmtCastExpr" )) |
69 | llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName() |
70 | << ")\n" ; |
71 | } |
72 | |
73 | void checkPreStmt(const ArraySubscriptExpr *SubExpr, |
74 | CheckerContext &C) const { |
75 | if (isCallbackEnabled(C, CallbackName: "PreStmtArraySubscriptExpr" )) |
76 | llvm::errs() << "PreStmt<ArraySubscriptExpr>\n" ; |
77 | } |
78 | |
79 | void checkPostStmt(const ArraySubscriptExpr *SubExpr, |
80 | CheckerContext &C) const { |
81 | if (isCallbackEnabled(C, CallbackName: "PostStmtArraySubscriptExpr" )) |
82 | llvm::errs() << "PostStmt<ArraySubscriptExpr>\n" ; |
83 | } |
84 | |
85 | void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const { |
86 | if (isCallbackEnabled(C, CallbackName: "PreStmtCXXNewExpr" )) |
87 | llvm::errs() << "PreStmt<CXXNewExpr>\n" ; |
88 | } |
89 | |
90 | void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { |
91 | if (isCallbackEnabled(C, CallbackName: "PostStmtCXXNewExpr" )) |
92 | llvm::errs() << "PostStmt<CXXNewExpr>\n" ; |
93 | } |
94 | |
95 | void checkPreStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { |
96 | if (isCallbackEnabled(C, CallbackName: "PreStmtCXXDeleteExpr" )) |
97 | llvm::errs() << "PreStmt<CXXDeleteExpr>\n" ; |
98 | } |
99 | |
100 | void checkPostStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { |
101 | if (isCallbackEnabled(C, CallbackName: "PostStmtCXXDeleteExpr" )) |
102 | llvm::errs() << "PostStmt<CXXDeleteExpr>\n" ; |
103 | } |
104 | |
105 | void checkPreStmt(const CXXConstructExpr *NE, CheckerContext &C) const { |
106 | if (isCallbackEnabled(C, CallbackName: "PreStmtCXXConstructExpr" )) |
107 | llvm::errs() << "PreStmt<CXXConstructExpr>\n" ; |
108 | } |
109 | |
110 | void checkPostStmt(const CXXConstructExpr *NE, CheckerContext &C) const { |
111 | if (isCallbackEnabled(C, CallbackName: "PostStmtCXXConstructExpr" )) |
112 | llvm::errs() << "PostStmt<CXXConstructExpr>\n" ; |
113 | } |
114 | |
115 | void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { |
116 | if (isCallbackEnabled(C, CallbackName: "PreStmtOffsetOfExpr" )) |
117 | llvm::errs() << "PreStmt<OffsetOfExpr>\n" ; |
118 | } |
119 | |
120 | void checkPostStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { |
121 | if (isCallbackEnabled(C, CallbackName: "PostStmtOffsetOfExpr" )) |
122 | llvm::errs() << "PostStmt<OffsetOfExpr>\n" ; |
123 | } |
124 | |
125 | bool evalCall(const CallEvent &Call, CheckerContext &C) const { |
126 | if (isCallbackEnabled(C, CallbackName: "EvalCall" )) { |
127 | llvm::errs() << "EvalCall" ; |
128 | if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Val: Call.getDecl())) |
129 | llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; |
130 | llvm::errs() << " {argno: " << Call.getNumArgs() << '}'; |
131 | llvm::errs() << " [" << Call.getKindAsString() << ']'; |
132 | llvm::errs() << '\n'; |
133 | return true; |
134 | } |
135 | return false; |
136 | } |
137 | |
138 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const { |
139 | if (isCallbackEnabled(C, CallbackName: "PreCall" )) { |
140 | llvm::errs() << "PreCall" ; |
141 | if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Val: Call.getDecl())) |
142 | llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; |
143 | llvm::errs() << " [" << Call.getKindAsString() << ']'; |
144 | llvm::errs() << '\n'; |
145 | } |
146 | } |
147 | |
148 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const { |
149 | if (isCallbackEnabled(C, CallbackName: "PostCall" )) { |
150 | llvm::errs() << "PostCall" ; |
151 | if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Val: Call.getDecl())) |
152 | llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; |
153 | llvm::errs() << " [" << Call.getKindAsString() << ']'; |
154 | llvm::errs() << '\n'; |
155 | } |
156 | } |
157 | |
158 | void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const { |
159 | if (isCallbackEnabled(C, CallbackName: "EndFunction" )) { |
160 | llvm::errs() << "EndFunction\nReturnStmt: " << (S ? "yes" : "no" ) << "\n" ; |
161 | if (!S) |
162 | return; |
163 | |
164 | llvm::errs() << "CFGElement: " ; |
165 | CFGStmtMap *Map = C.getCurrentAnalysisDeclContext()->getCFGStmtMap(); |
166 | CFGElement LastElement = Map->getBlock(S)->back(); |
167 | |
168 | if (LastElement.getAs<CFGStmt>()) |
169 | llvm::errs() << "CFGStmt\n" ; |
170 | else if (LastElement.getAs<CFGAutomaticObjDtor>()) |
171 | llvm::errs() << "CFGAutomaticObjDtor\n" ; |
172 | } |
173 | } |
174 | |
175 | void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, |
176 | ExprEngine &Eng) const { |
177 | if (isCallbackEnabled(Opts: BR.getAnalyzerOptions(), CallbackName: "EndAnalysis" )) |
178 | llvm::errs() << "EndAnalysis\n" ; |
179 | } |
180 | |
181 | void checkNewAllocator(const CXXAllocatorCall &Call, |
182 | CheckerContext &C) const { |
183 | if (isCallbackEnabled(C, CallbackName: "NewAllocator" )) |
184 | llvm::errs() << "NewAllocator\n" ; |
185 | } |
186 | |
187 | void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { |
188 | if (isCallbackEnabled(C, CallbackName: "Bind" )) |
189 | llvm::errs() << "Bind\n" ; |
190 | } |
191 | |
192 | void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const { |
193 | if (isCallbackEnabled(State, CallbackName: "LiveSymbols" )) |
194 | llvm::errs() << "LiveSymbols\n" ; |
195 | } |
196 | |
197 | ProgramStateRef |
198 | checkRegionChanges(ProgramStateRef State, |
199 | const InvalidatedSymbols *Invalidated, |
200 | ArrayRef<const MemRegion *> ExplicitRegions, |
201 | ArrayRef<const MemRegion *> Regions, |
202 | const LocationContext *LCtx, const CallEvent *Call) const { |
203 | if (isCallbackEnabled(State, CallbackName: "RegionChanges" )) |
204 | llvm::errs() << "RegionChanges\n" ; |
205 | return State; |
206 | } |
207 | |
208 | ProgramStateRef checkPointerEscape(ProgramStateRef State, |
209 | const InvalidatedSymbols &Escaped, |
210 | const CallEvent *Call, |
211 | PointerEscapeKind Kind) const { |
212 | if (isCallbackEnabled(State, CallbackName: "PointerEscape" )) |
213 | llvm::errs() << "PointerEscape\n" ; |
214 | return State; |
215 | } |
216 | }; |
217 | } // end anonymous namespace |
218 | |
219 | //===----------------------------------------------------------------------===// |
220 | // Registration. |
221 | //===----------------------------------------------------------------------===// |
222 | |
223 | void ento::registerAnalysisOrderChecker(CheckerManager &mgr) { |
224 | mgr.registerChecker<AnalysisOrderChecker>(); |
225 | } |
226 | |
227 | bool ento::shouldRegisterAnalysisOrderChecker(const CheckerManager &mgr) { |
228 | return true; |
229 | } |
230 | |