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