1//===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===//
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 "CheckerRegistration.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/CheckerRegistryData.h"
15#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.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 "llvm/Support/raw_ostream.h"
21#include "gtest/gtest.h"
22#include <memory>
23
24namespace clang {
25namespace ento {
26namespace {
27
28//===----------------------------------------------------------------------===//
29// Just a minimal test for how checker registration works with statically
30// linked, non TableGen generated checkers.
31//===----------------------------------------------------------------------===//
32
33class CustomChecker : public Checker<check::ASTCodeBody> {
34public:
35 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
36 BugReporter &BR) const {
37 BR.EmitBasicReport(DeclWithIssue: D, Checker: this, BugName: "Custom diagnostic", BugCategory: categories::LogicError,
38 BugStr: "Custom diagnostic description",
39 Loc: PathDiagnosticLocation(D, Mgr.getSourceManager()), Ranges: {});
40 }
41};
42
43void addCustomChecker(AnalysisASTConsumer &AnalysisConsumer,
44 AnalyzerOptions &AnOpts) {
45 AnOpts.CheckersAndPackages = {{"test.CustomChecker", true}};
46 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
47 Registry.addChecker<CustomChecker>(FullName: "test.CustomChecker", Desc: "Description", DocsUri: "");
48 });
49}
50
51TEST(RegisterCustomCheckers, RegisterChecker) {
52 std::string Diags;
53 EXPECT_TRUE(runCheckerOnCode<addCustomChecker>("void f() {;}", Diags));
54 EXPECT_EQ(Diags, "test.CustomChecker: Custom diagnostic description\n");
55}
56
57//===----------------------------------------------------------------------===//
58// Pretty much the same.
59//===----------------------------------------------------------------------===//
60
61class LocIncDecChecker : public Checker<check::Location> {
62public:
63 void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
64 CheckerContext &C) const {
65 const auto *UnaryOp = dyn_cast<UnaryOperator>(Val: S);
66 if (UnaryOp && !IsLoad) {
67 EXPECT_FALSE(UnaryOp->isIncrementOp());
68 }
69 }
70};
71
72void addLocIncDecChecker(AnalysisASTConsumer &AnalysisConsumer,
73 AnalyzerOptions &AnOpts) {
74 AnOpts.CheckersAndPackages = {{"test.LocIncDecChecker", true}};
75 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
76 Registry.addChecker<CustomChecker>(FullName: "test.LocIncDecChecker", Desc: "Description",
77 DocsUri: "");
78 });
79}
80
81TEST(RegisterCustomCheckers, CheckLocationIncDec) {
82 EXPECT_TRUE(
83 runCheckerOnCode<addLocIncDecChecker>("void f() { int *p; (*p)++; }"));
84}
85
86//===----------------------------------------------------------------------===//
87// Unsatisfied checker dependency
88//===----------------------------------------------------------------------===//
89
90class CheckerRegistrationOrderPrinter
91 : public Checker<check::PreStmt<DeclStmt>> {
92 const BugType BT{this, "Registration order"};
93
94public:
95 void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
96 ExplodedNode *N = nullptr;
97 N = C.generateErrorNode();
98 llvm::SmallString<200> Buf;
99 llvm::raw_svector_ostream OS(Buf);
100 C.getAnalysisManager()
101 .getCheckerManager()
102 ->getCheckerRegistryData()
103 .printEnabledCheckerList(Out&: OS);
104 // Strip a newline off.
105 auto R =
106 std::make_unique<PathSensitiveBugReport>(args: BT, args: OS.str().drop_back(N: 1), args&: N);
107 C.emitReport(R: std::move(R));
108 }
109};
110
111void registerCheckerRegistrationOrderPrinter(CheckerManager &mgr) {
112 mgr.registerChecker<CheckerRegistrationOrderPrinter>();
113}
114
115bool shouldRegisterCheckerRegistrationOrderPrinter(const CheckerManager &mgr) {
116 return true;
117}
118
119void addCheckerRegistrationOrderPrinter(CheckerRegistry &Registry) {
120 Registry.addChecker(Fn: registerCheckerRegistrationOrderPrinter,
121 sfn: shouldRegisterCheckerRegistrationOrderPrinter,
122 FullName: "test.RegistrationOrder", Desc: "Description", DocsUri: "", IsHidden: false);
123}
124
125#define UNITTEST_CHECKER(CHECKER_NAME, DIAG_MSG) \
126 class CHECKER_NAME : public Checker<check::PreStmt<DeclStmt>> { \
127 public: \
128 void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {} \
129 }; \
130 \
131 void register##CHECKER_NAME(CheckerManager &mgr) { \
132 mgr.registerChecker<CHECKER_NAME>(); \
133 } \
134 \
135 bool shouldRegister##CHECKER_NAME(const CheckerManager &mgr) { \
136 return true; \
137 } \
138 void add##CHECKER_NAME(CheckerRegistry &Registry) { \
139 Registry.addChecker(register##CHECKER_NAME, shouldRegister##CHECKER_NAME, \
140 "test." #CHECKER_NAME, "Description", "", false); \
141 }
142
143UNITTEST_CHECKER(StrongDep, "Strong")
144UNITTEST_CHECKER(Dep, "Dep")
145
146bool shouldRegisterStrongFALSE(const CheckerManager &mgr) {
147 return false;
148}
149
150
151void addDep(AnalysisASTConsumer &AnalysisConsumer,
152 AnalyzerOptions &AnOpts) {
153 AnOpts.CheckersAndPackages = {{"test.Dep", true},
154 {"test.RegistrationOrder", true}};
155 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) {
156 Registry.addChecker(Fn: registerStrongDep, sfn: shouldRegisterStrongFALSE,
157 FullName: "test.Strong", Desc: "Description", DocsUri: "", IsHidden: false);
158 addStrongDep(Registry);
159 addDep(Registry);
160 addCheckerRegistrationOrderPrinter(Registry);
161 Registry.addDependency(FullName: "test.Dep", Dependency: "test.Strong");
162 });
163}
164
165TEST(RegisterDeps, UnsatisfiedDependency) {
166 std::string Diags;
167 EXPECT_TRUE(runCheckerOnCode<addDep>("void f() {int i;}", Diags));
168 EXPECT_EQ(Diags, "test.RegistrationOrder: test.RegistrationOrder\n");
169}
170
171//===----------------------------------------------------------------------===//
172// Weak checker dependencies.
173//===----------------------------------------------------------------------===//
174
175UNITTEST_CHECKER(WeakDep, "Weak")
176
177void addWeakDepCheckerBothEnabled(AnalysisASTConsumer &AnalysisConsumer,
178 AnalyzerOptions &AnOpts) {
179 AnOpts.CheckersAndPackages = {{"test.Dep", true},
180 {"test.WeakDep", true},
181 {"test.RegistrationOrder", true}};
182 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
183 addWeakDep(Registry);
184 addDep(Registry);
185 addCheckerRegistrationOrderPrinter(Registry);
186 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
187 });
188}
189
190void addWeakDepCheckerBothEnabledSwitched(AnalysisASTConsumer &AnalysisConsumer,
191 AnalyzerOptions &AnOpts) {
192 AnOpts.CheckersAndPackages = {{"test.Dep", true},
193 {"test.WeakDep", true},
194 {"test.RegistrationOrder", true}};
195 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
196 addWeakDep(Registry);
197 addDep(Registry);
198 addCheckerRegistrationOrderPrinter(Registry);
199 Registry.addWeakDependency(FullName: "test.WeakDep", Dependency: "test.Dep");
200 });
201}
202
203void addWeakDepCheckerDepDisabled(AnalysisASTConsumer &AnalysisConsumer,
204 AnalyzerOptions &AnOpts) {
205 AnOpts.CheckersAndPackages = {{"test.Dep", true},
206 {"test.WeakDep", false},
207 {"test.RegistrationOrder", true}};
208 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
209 addWeakDep(Registry);
210 addDep(Registry);
211 addCheckerRegistrationOrderPrinter(Registry);
212 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
213 });
214}
215
216void addWeakDepCheckerDepUnspecified(AnalysisASTConsumer &AnalysisConsumer,
217 AnalyzerOptions &AnOpts) {
218 AnOpts.CheckersAndPackages = {{"test.Dep", true},
219 {"test.RegistrationOrder", true}};
220 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
221 addWeakDep(Registry);
222 addDep(Registry);
223 addCheckerRegistrationOrderPrinter(Registry);
224 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
225 });
226}
227
228UNITTEST_CHECKER(WeakDep2, "Weak2")
229UNITTEST_CHECKER(Dep2, "Dep2")
230
231void addWeakDepHasWeakDep(AnalysisASTConsumer &AnalysisConsumer,
232 AnalyzerOptions &AnOpts) {
233 AnOpts.CheckersAndPackages = {{"test.Dep", true},
234 {"test.WeakDep", true},
235 {"test.WeakDep2", true},
236 {"test.RegistrationOrder", true}};
237 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
238 addStrongDep(Registry);
239 addWeakDep(Registry);
240 addWeakDep2(Registry);
241 addDep(Registry);
242 addDep2(Registry);
243 addCheckerRegistrationOrderPrinter(Registry);
244 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
245 Registry.addWeakDependency(FullName: "test.WeakDep", Dependency: "test.WeakDep2");
246 });
247}
248
249void addWeakDepTransitivity(AnalysisASTConsumer &AnalysisConsumer,
250 AnalyzerOptions &AnOpts) {
251 AnOpts.CheckersAndPackages = {{"test.Dep", true},
252 {"test.WeakDep", false},
253 {"test.WeakDep2", true},
254 {"test.RegistrationOrder", true}};
255 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
256 addStrongDep(Registry);
257 addWeakDep(Registry);
258 addWeakDep2(Registry);
259 addDep(Registry);
260 addDep2(Registry);
261 addCheckerRegistrationOrderPrinter(Registry);
262 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
263 Registry.addWeakDependency(FullName: "test.WeakDep", Dependency: "test.WeakDep2");
264 });
265}
266
267TEST(RegisterDeps, SimpleWeakDependency) {
268 std::string Diags;
269 EXPECT_TRUE(runCheckerOnCode<addWeakDepCheckerBothEnabled>(
270 "void f() {int i;}", Diags));
271 EXPECT_EQ(Diags, "test.RegistrationOrder: test.WeakDep\ntest."
272 "Dep\ntest.RegistrationOrder\n");
273 Diags.clear();
274
275 // Mind that AnalyzerOption listed the enabled checker list in the same order,
276 // but the dependencies are switched.
277 EXPECT_TRUE(runCheckerOnCode<addWeakDepCheckerBothEnabledSwitched>(
278 "void f() {int i;}", Diags));
279 EXPECT_EQ(Diags, "test.RegistrationOrder: test.Dep\ntest."
280 "RegistrationOrder\ntest.WeakDep\n");
281 Diags.clear();
282
283 // Weak dependencies dont prevent dependent checkers from being enabled.
284 EXPECT_TRUE(runCheckerOnCode<addWeakDepCheckerDepDisabled>(
285 "void f() {int i;}", Diags));
286 EXPECT_EQ(Diags,
287 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
288 Diags.clear();
289
290 // Nor will they be enabled just because a dependent checker is.
291 EXPECT_TRUE(runCheckerOnCode<addWeakDepCheckerDepUnspecified>(
292 "void f() {int i;}", Diags));
293 EXPECT_EQ(Diags,
294 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
295 Diags.clear();
296
297 EXPECT_TRUE(
298 runCheckerOnCode<addWeakDepTransitivity>("void f() {int i;}", Diags));
299 EXPECT_EQ(Diags, "test.RegistrationOrder: test.WeakDep2\ntest."
300 "Dep\ntest.RegistrationOrder\n");
301 Diags.clear();
302
303 EXPECT_TRUE(
304 runCheckerOnCode<addWeakDepHasWeakDep>("void f() {int i;}", Diags));
305 EXPECT_EQ(Diags, "test.RegistrationOrder: test.WeakDep2\ntest."
306 "WeakDep\ntest.Dep\ntest.RegistrationOrder\n");
307 Diags.clear();
308}
309
310//===----------------------------------------------------------------------===//
311// Interaction of weak and regular checker dependencies.
312//===----------------------------------------------------------------------===//
313
314void addWeakDepHasStrongDep(AnalysisASTConsumer &AnalysisConsumer,
315 AnalyzerOptions &AnOpts) {
316 AnOpts.CheckersAndPackages = {{"test.Dep", true},
317 {"test.StrongDep", true},
318 {"test.WeakDep", true},
319 {"test.RegistrationOrder", true}};
320 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
321 addStrongDep(Registry);
322 addWeakDep(Registry);
323 addDep(Registry);
324 addCheckerRegistrationOrderPrinter(Registry);
325 Registry.addDependency(FullName: "test.WeakDep", Dependency: "test.StrongDep");
326 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
327 });
328}
329
330void addWeakDepAndStrongDep(AnalysisASTConsumer &AnalysisConsumer,
331 AnalyzerOptions &AnOpts) {
332 AnOpts.CheckersAndPackages = {{"test.Dep", true},
333 {"test.StrongDep", true},
334 {"test.WeakDep", true},
335 {"test.RegistrationOrder", true}};
336 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
337 addStrongDep(Registry);
338 addWeakDep(Registry);
339 addDep(Registry);
340 addCheckerRegistrationOrderPrinter(Registry);
341 Registry.addDependency(FullName: "test.Dep", Dependency: "test.StrongDep");
342 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
343 });
344}
345
346void addDisabledWeakDepHasStrongDep(AnalysisASTConsumer &AnalysisConsumer,
347 AnalyzerOptions &AnOpts) {
348 AnOpts.CheckersAndPackages = {{"test.Dep", true},
349 {"test.StrongDep", true},
350 {"test.WeakDep", false},
351 {"test.RegistrationOrder", true}};
352 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
353 addStrongDep(Registry);
354 addWeakDep(Registry);
355 addDep(Registry);
356 addCheckerRegistrationOrderPrinter(Registry);
357 Registry.addDependency(FullName: "test.WeakDep", Dependency: "test.StrongDep");
358 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
359 });
360}
361
362void addDisabledWeakDepHasUnspecifiedStrongDep(
363 AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) {
364 AnOpts.CheckersAndPackages = {{"test.Dep", true},
365 {"test.WeakDep", false},
366 {"test.RegistrationOrder", true}};
367 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
368 addStrongDep(Registry);
369 addWeakDep(Registry);
370 addDep(Registry);
371 addCheckerRegistrationOrderPrinter(Registry);
372 Registry.addDependency(FullName: "test.WeakDep", Dependency: "test.StrongDep");
373 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
374 });
375}
376
377void addWeakDepHasDisabledStrongDep(AnalysisASTConsumer &AnalysisConsumer,
378 AnalyzerOptions &AnOpts) {
379 AnOpts.CheckersAndPackages = {{"test.Dep", true},
380 {"test.StrongDep", false},
381 {"test.WeakDep", true},
382 {"test.RegistrationOrder", true}};
383 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
384 addStrongDep(Registry);
385 addWeakDep(Registry);
386 addDep(Registry);
387 addCheckerRegistrationOrderPrinter(Registry);
388 Registry.addDependency(FullName: "test.WeakDep", Dependency: "test.StrongDep");
389 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
390 });
391}
392
393void addWeakDepHasUnspecifiedButLaterEnabledStrongDep(
394 AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) {
395 AnOpts.CheckersAndPackages = {{"test.Dep", true},
396 {"test.Dep2", true},
397 {"test.WeakDep", true},
398 {"test.RegistrationOrder", true}};
399 AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) {
400 addStrongDep(Registry);
401 addWeakDep(Registry);
402 addDep(Registry);
403 addDep2(Registry);
404 addCheckerRegistrationOrderPrinter(Registry);
405 Registry.addDependency(FullName: "test.WeakDep", Dependency: "test.StrongDep");
406 Registry.addDependency(FullName: "test.Dep2", Dependency: "test.StrongDep");
407 Registry.addWeakDependency(FullName: "test.Dep", Dependency: "test.WeakDep");
408 });
409}
410
411TEST(RegisterDeps, DependencyInteraction) {
412 std::string Diags;
413 EXPECT_TRUE(
414 runCheckerOnCode<addWeakDepHasStrongDep>("void f() {int i;}", Diags));
415 EXPECT_EQ(Diags, "test.RegistrationOrder: test.StrongDep\ntest."
416 "WeakDep\ntest.Dep\ntest.RegistrationOrder\n");
417 Diags.clear();
418
419 // Weak dependencies are registered before strong dependencies. This is most
420 // important for purely diagnostic checkers that are implemented as a part of
421 // purely modeling checkers, becuse the checker callback order will have to be
422 // established in between the modeling portion and the weak dependency.
423 EXPECT_TRUE(
424 runCheckerOnCode<addWeakDepAndStrongDep>("void f() {int i;}", Diags));
425 EXPECT_EQ(Diags, "test.RegistrationOrder: test.WeakDep\ntest."
426 "StrongDep\ntest.Dep\ntest.RegistrationOrder\n");
427 Diags.clear();
428
429 // If a weak dependency is disabled, the checker itself can still be enabled.
430 EXPECT_TRUE(runCheckerOnCode<addDisabledWeakDepHasStrongDep>(
431 "void f() {int i;}", Diags));
432 EXPECT_EQ(Diags, "test.RegistrationOrder: test.Dep\ntest."
433 "RegistrationOrder\ntest.StrongDep\n");
434 Diags.clear();
435
436 // If a weak dependency is disabled, the checker itself can still be enabled,
437 // but it shouldn't enable a strong unspecified dependency.
438 EXPECT_TRUE(runCheckerOnCode<addDisabledWeakDepHasUnspecifiedStrongDep>(
439 "void f() {int i;}", Diags));
440 EXPECT_EQ(Diags,
441 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
442 Diags.clear();
443
444 // A strong dependency of a weak dependency is disabled, so neither of them
445 // should be enabled.
446 EXPECT_TRUE(runCheckerOnCode<addWeakDepHasDisabledStrongDep>(
447 "void f() {int i;}", Diags));
448 EXPECT_EQ(Diags,
449 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
450 Diags.clear();
451
452 EXPECT_TRUE(
453 runCheckerOnCode<addWeakDepHasUnspecifiedButLaterEnabledStrongDep>(
454 "void f() {int i;}", Diags));
455 EXPECT_EQ(Diags, "test.RegistrationOrder: test.StrongDep\ntest.WeakDep\ntest."
456 "Dep\ntest.Dep2\ntest.RegistrationOrder\n");
457 Diags.clear();
458}
459} // namespace
460} // namespace ento
461} // namespace clang
462

source code of clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp