1 //===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===//
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
7 //===----------------------------------------------------------------------===//
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"
28 //===----------------------------------------------------------------------===//
29 // Just a minimal test for how checker registration works with statically
30 // linked, non TableGen generated checkers.
31 //===----------------------------------------------------------------------===//
33 class CustomChecker
: public Checker
<check::ASTCodeBody
> {
35 void checkASTCodeBody(const Decl
*D
, AnalysisManager
&Mgr
,
36 BugReporter
&BR
) const {
37 BR
.EmitBasicReport(D
, this, "Custom diagnostic", categories::LogicError
,
38 "Custom diagnostic description",
39 PathDiagnosticLocation(D
, Mgr
.getSourceManager()), {});
43 void addCustomChecker(AnalysisASTConsumer
&AnalysisConsumer
,
44 AnalyzerOptions
&AnOpts
) {
45 AnOpts
.CheckersAndPackages
= {{"test.CustomChecker", true}};
46 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
47 Registry
.addChecker
<CustomChecker
>("test.CustomChecker", "Description", "");
51 TEST(RegisterCustomCheckers
, RegisterChecker
) {
53 EXPECT_TRUE(runCheckerOnCode
<addCustomChecker
>("void f() {;}", Diags
));
54 EXPECT_EQ(Diags
, "test.CustomChecker: Custom diagnostic description\n");
57 //===----------------------------------------------------------------------===//
58 // Pretty much the same.
59 //===----------------------------------------------------------------------===//
61 class LocIncDecChecker
: public Checker
<check::Location
> {
63 void checkLocation(SVal Loc
, bool IsLoad
, const Stmt
*S
,
64 CheckerContext
&C
) const {
65 const auto *UnaryOp
= dyn_cast
<UnaryOperator
>(S
);
66 if (UnaryOp
&& !IsLoad
) {
67 EXPECT_FALSE(UnaryOp
->isIncrementOp());
72 void addLocIncDecChecker(AnalysisASTConsumer
&AnalysisConsumer
,
73 AnalyzerOptions
&AnOpts
) {
74 AnOpts
.CheckersAndPackages
= {{"test.LocIncDecChecker", true}};
75 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
76 Registry
.addChecker
<CustomChecker
>("test.LocIncDecChecker", "Description",
81 TEST(RegisterCustomCheckers
, CheckLocationIncDec
) {
83 runCheckerOnCode
<addLocIncDecChecker
>("void f() { int *p; (*p)++; }"));
86 //===----------------------------------------------------------------------===//
87 // Unsatisfied checker dependency
88 //===----------------------------------------------------------------------===//
90 class CheckerRegistrationOrderPrinter
91 : public Checker
<check::PreStmt
<DeclStmt
>> {
92 const BugType BT
{this, "Registration order"};
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()
102 ->getCheckerRegistryData()
103 .printEnabledCheckerList(OS
);
104 // Strip a newline off.
106 std::make_unique
<PathSensitiveBugReport
>(BT
, OS
.str().drop_back(1), N
);
107 C
.emitReport(std::move(R
));
111 void registerCheckerRegistrationOrderPrinter(CheckerManager
&mgr
) {
112 mgr
.registerChecker
<CheckerRegistrationOrderPrinter
>();
115 bool shouldRegisterCheckerRegistrationOrderPrinter(const CheckerManager
&mgr
) {
119 void addCheckerRegistrationOrderPrinter(CheckerRegistry
&Registry
) {
120 Registry
.addChecker(registerCheckerRegistrationOrderPrinter
,
121 shouldRegisterCheckerRegistrationOrderPrinter
,
122 "test.RegistrationOrder", "Description", "", false);
125 #define UNITTEST_CHECKER(CHECKER_NAME, DIAG_MSG) \
126 class CHECKER_NAME : public Checker<check::PreStmt<DeclStmt>> { \
128 void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {} \
131 void register##CHECKER_NAME(CheckerManager &mgr) { \
132 mgr.registerChecker<CHECKER_NAME>(); \
135 bool shouldRegister##CHECKER_NAME(const CheckerManager &mgr) { \
138 void add##CHECKER_NAME(CheckerRegistry &Registry) { \
139 Registry.addChecker(register##CHECKER_NAME, shouldRegister##CHECKER_NAME, \
140 "test." #CHECKER_NAME, "Description", "", false); \
143 UNITTEST_CHECKER(StrongDep
, "Strong")
144 UNITTEST_CHECKER(Dep
, "Dep")
146 bool shouldRegisterStrongFALSE(const CheckerManager
&mgr
) {
151 void addDep(AnalysisASTConsumer
&AnalysisConsumer
,
152 AnalyzerOptions
&AnOpts
) {
153 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
154 {"test.RegistrationOrder", true}};
155 AnalysisConsumer
.AddCheckerRegistrationFn([](CheckerRegistry
&Registry
) {
156 Registry
.addChecker(registerStrongDep
, shouldRegisterStrongFALSE
,
157 "test.Strong", "Description", "", false);
158 addStrongDep(Registry
);
160 addCheckerRegistrationOrderPrinter(Registry
);
161 Registry
.addDependency("test.Dep", "test.Strong");
165 TEST(RegisterDeps
, UnsatisfiedDependency
) {
167 EXPECT_TRUE(runCheckerOnCode
<addDep
>("void f() {int i;}", Diags
));
168 EXPECT_EQ(Diags
, "test.RegistrationOrder: test.RegistrationOrder\n");
171 //===----------------------------------------------------------------------===//
172 // Weak checker dependencies.
173 //===----------------------------------------------------------------------===//
175 UNITTEST_CHECKER(WeakDep
, "Weak")
177 void addWeakDepCheckerBothEnabled(AnalysisASTConsumer
&AnalysisConsumer
,
178 AnalyzerOptions
&AnOpts
) {
179 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
180 {"test.WeakDep", true},
181 {"test.RegistrationOrder", true}};
182 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
183 addWeakDep(Registry
);
185 addCheckerRegistrationOrderPrinter(Registry
);
186 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
190 void addWeakDepCheckerBothEnabledSwitched(AnalysisASTConsumer
&AnalysisConsumer
,
191 AnalyzerOptions
&AnOpts
) {
192 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
193 {"test.WeakDep", true},
194 {"test.RegistrationOrder", true}};
195 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
196 addWeakDep(Registry
);
198 addCheckerRegistrationOrderPrinter(Registry
);
199 Registry
.addWeakDependency("test.WeakDep", "test.Dep");
203 void addWeakDepCheckerDepDisabled(AnalysisASTConsumer
&AnalysisConsumer
,
204 AnalyzerOptions
&AnOpts
) {
205 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
206 {"test.WeakDep", false},
207 {"test.RegistrationOrder", true}};
208 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
209 addWeakDep(Registry
);
211 addCheckerRegistrationOrderPrinter(Registry
);
212 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
216 void addWeakDepCheckerDepUnspecified(AnalysisASTConsumer
&AnalysisConsumer
,
217 AnalyzerOptions
&AnOpts
) {
218 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
219 {"test.RegistrationOrder", true}};
220 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
221 addWeakDep(Registry
);
223 addCheckerRegistrationOrderPrinter(Registry
);
224 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
228 UNITTEST_CHECKER(WeakDep2
, "Weak2")
229 UNITTEST_CHECKER(Dep2
, "Dep2")
231 void 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([=](CheckerRegistry
&Registry
) {
238 addStrongDep(Registry
);
239 addWeakDep(Registry
);
240 addWeakDep2(Registry
);
243 addCheckerRegistrationOrderPrinter(Registry
);
244 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
245 Registry
.addWeakDependency("test.WeakDep", "test.WeakDep2");
249 void 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([=](CheckerRegistry
&Registry
) {
256 addStrongDep(Registry
);
257 addWeakDep(Registry
);
258 addWeakDep2(Registry
);
261 addCheckerRegistrationOrderPrinter(Registry
);
262 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
263 Registry
.addWeakDependency("test.WeakDep", "test.WeakDep2");
267 TEST(RegisterDeps
, SimpleWeakDependency
) {
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");
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");
283 // Weak dependencies dont prevent dependent checkers from being enabled.
284 EXPECT_TRUE(runCheckerOnCode
<addWeakDepCheckerDepDisabled
>(
285 "void f() {int i;}", Diags
));
287 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
290 // Nor will they be enabled just because a dependent checker is.
291 EXPECT_TRUE(runCheckerOnCode
<addWeakDepCheckerDepUnspecified
>(
292 "void f() {int i;}", Diags
));
294 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
298 runCheckerOnCode
<addWeakDepTransitivity
>("void f() {int i;}", Diags
));
299 EXPECT_EQ(Diags
, "test.RegistrationOrder: test.WeakDep2\ntest."
300 "Dep\ntest.RegistrationOrder\n");
304 runCheckerOnCode
<addWeakDepHasWeakDep
>("void f() {int i;}", Diags
));
305 EXPECT_EQ(Diags
, "test.RegistrationOrder: test.WeakDep2\ntest."
306 "WeakDep\ntest.Dep\ntest.RegistrationOrder\n");
310 //===----------------------------------------------------------------------===//
311 // Interaction of weak and regular checker dependencies.
312 //===----------------------------------------------------------------------===//
314 void 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([=](CheckerRegistry
&Registry
) {
321 addStrongDep(Registry
);
322 addWeakDep(Registry
);
324 addCheckerRegistrationOrderPrinter(Registry
);
325 Registry
.addDependency("test.WeakDep", "test.StrongDep");
326 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
330 void 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([=](CheckerRegistry
&Registry
) {
337 addStrongDep(Registry
);
338 addWeakDep(Registry
);
340 addCheckerRegistrationOrderPrinter(Registry
);
341 Registry
.addDependency("test.Dep", "test.StrongDep");
342 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
346 void 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([=](CheckerRegistry
&Registry
) {
353 addStrongDep(Registry
);
354 addWeakDep(Registry
);
356 addCheckerRegistrationOrderPrinter(Registry
);
357 Registry
.addDependency("test.WeakDep", "test.StrongDep");
358 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
362 void addDisabledWeakDepHasUnspecifiedStrongDep(
363 AnalysisASTConsumer
&AnalysisConsumer
, AnalyzerOptions
&AnOpts
) {
364 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
365 {"test.WeakDep", false},
366 {"test.RegistrationOrder", true}};
367 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
368 addStrongDep(Registry
);
369 addWeakDep(Registry
);
371 addCheckerRegistrationOrderPrinter(Registry
);
372 Registry
.addDependency("test.WeakDep", "test.StrongDep");
373 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
377 void 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([=](CheckerRegistry
&Registry
) {
384 addStrongDep(Registry
);
385 addWeakDep(Registry
);
387 addCheckerRegistrationOrderPrinter(Registry
);
388 Registry
.addDependency("test.WeakDep", "test.StrongDep");
389 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
393 void addWeakDepHasUnspecifiedButLaterEnabledStrongDep(
394 AnalysisASTConsumer
&AnalysisConsumer
, AnalyzerOptions
&AnOpts
) {
395 AnOpts
.CheckersAndPackages
= {{"test.Dep", true},
397 {"test.WeakDep", true},
398 {"test.RegistrationOrder", true}};
399 AnalysisConsumer
.AddCheckerRegistrationFn([=](CheckerRegistry
&Registry
) {
400 addStrongDep(Registry
);
401 addWeakDep(Registry
);
404 addCheckerRegistrationOrderPrinter(Registry
);
405 Registry
.addDependency("test.WeakDep", "test.StrongDep");
406 Registry
.addDependency("test.Dep2", "test.StrongDep");
407 Registry
.addWeakDependency("test.Dep", "test.WeakDep");
411 TEST(RegisterDeps
, DependencyInteraction
) {
414 runCheckerOnCode
<addWeakDepHasStrongDep
>("void f() {int i;}", Diags
));
415 EXPECT_EQ(Diags
, "test.RegistrationOrder: test.StrongDep\ntest."
416 "WeakDep\ntest.Dep\ntest.RegistrationOrder\n");
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.
424 runCheckerOnCode
<addWeakDepAndStrongDep
>("void f() {int i;}", Diags
));
425 EXPECT_EQ(Diags
, "test.RegistrationOrder: test.WeakDep\ntest."
426 "StrongDep\ntest.Dep\ntest.RegistrationOrder\n");
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");
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
));
441 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
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
));
449 "test.RegistrationOrder: test.Dep\ntest.RegistrationOrder\n");
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");