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