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 "gtest/gtest.h"
20 
21 namespace clang {
22 namespace ento {
23 namespace {
24 
25 //===----------------------------------------------------------------------===//
26 // Just a minimal test for how checker registration works with statically
27 // linked, non TableGen generated checkers.
28 //===----------------------------------------------------------------------===//
29 
30 class CustomChecker : public Checker<check::ASTCodeBody> {
31 public:
32   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
33                         BugReporter &BR) const {
34     BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError,
35                        "Custom diagnostic description",
36                        PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
37   }
38 };
39 
40 void addCustomChecker(AnalysisASTConsumer &AnalysisConsumer,
41                       AnalyzerOptions &AnOpts) {
42   AnOpts.CheckersAndPackages = {{"custom.CustomChecker", true}};
43   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
44     Registry.addChecker<CustomChecker>("custom.CustomChecker", "Description",
45                                        "");
46   });
47 }
48 
49 TEST(RegisterCustomCheckers, RegisterChecker) {
50   std::string Diags;
51   EXPECT_TRUE(runCheckerOnCode<addCustomChecker>("void f() {;}", Diags));
52   EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description\n");
53 }
54 
55 //===----------------------------------------------------------------------===//
56 // Pretty much the same.
57 //===----------------------------------------------------------------------===//
58 
59 class LocIncDecChecker : public Checker<check::Location> {
60 public:
61   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
62                      CheckerContext &C) const {
63     auto UnaryOp = dyn_cast<UnaryOperator>(S);
64     if (UnaryOp && !IsLoad) {
65       EXPECT_FALSE(UnaryOp->isIncrementOp());
66     }
67   }
68 };
69 
70 void addLocIncDecChecker(AnalysisASTConsumer &AnalysisConsumer,
71                          AnalyzerOptions &AnOpts) {
72   AnOpts.CheckersAndPackages = {{"test.LocIncDecChecker", true}};
73   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
74     Registry.addChecker<CustomChecker>("test.LocIncDecChecker", "Description",
75                                        "");
76   });
77 }
78 
79 TEST(RegisterCustomCheckers, CheckLocationIncDec) {
80   EXPECT_TRUE(
81       runCheckerOnCode<addLocIncDecChecker>("void f() { int *p; (*p)++; }"));
82 }
83 
84 //===----------------------------------------------------------------------===//
85 // Unsatisfied checker dependency
86 //===----------------------------------------------------------------------===//
87 
88 class PrerequisiteChecker : public Checker<check::ASTCodeBody> {
89 public:
90   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
91                         BugReporter &BR) const {
92     BR.EmitBasicReport(D, this, "Prerequisite", categories::LogicError,
93                        "This is the prerequisite checker",
94                        PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
95   }
96 };
97 
98 void registerPrerequisiteChecker(CheckerManager &mgr) {
99   mgr.registerChecker<PrerequisiteChecker>();
100 }
101 
102 bool shouldRegisterPrerequisiteChecker(const CheckerManager &mgr) {
103   return false;
104 }
105 
106 class DependentChecker : public Checker<check::ASTCodeBody> {
107 public:
108   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
109                         BugReporter &BR) const {
110     BR.EmitBasicReport(D, this, "Dependent", categories::LogicError,
111                        "This is the Dependent Checker",
112                        PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
113   }
114 };
115 
116 void registerDependentChecker(CheckerManager &mgr) {
117   mgr.registerChecker<DependentChecker>();
118 }
119 
120 bool shouldRegisterDependentChecker(const CheckerManager &mgr) {
121   return true;
122 }
123 
124 void addDependentChecker(AnalysisASTConsumer &AnalysisConsumer,
125                          AnalyzerOptions &AnOpts) {
126   AnOpts.CheckersAndPackages = {{"custom.Dependent", true}};
127   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
128      Registry.addChecker(registerPrerequisiteChecker,
129                          shouldRegisterPrerequisiteChecker,
130                          "custom.Prerequisite", "Description", "", false);
131      Registry.addChecker(registerDependentChecker,
132                          shouldRegisterDependentChecker,
133                          "custom.Dependent", "Description", "", false);
134      Registry.addDependency("custom.Dependent", "custom.Prerequisite");
135     });
136 }
137 
138 TEST(RegisterDependentCheckers, RegisterChecker) {
139   std::string Diags;
140   EXPECT_TRUE(runCheckerOnCode<addDependentChecker>("void f() {;}", Diags));
141   EXPECT_EQ(Diags, "");
142 }
143 
144 } // namespace
145 } // namespace ento
146 } // namespace clang
147