462 lines · cpp
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-exception6//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 statically30// 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(D, this, "Custom diagnostic", categories::LogicError,38 "Custom diagnostic description",39 PathDiagnosticLocation(D, Mgr.getSourceManager()), {});40 }41};42 43void addCustomChecker(AnalysisASTConsumer &AnalysisConsumer,44 AnalyzerOptions &AnOpts) {45 AnOpts.CheckersAndPackages = {{"test.CustomChecker", true}};46 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {47 Registry.addChecker<CustomChecker>("test.CustomChecker", "MockDescription");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>(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([](CheckerRegistry &Registry) {76 Registry.addChecker<CustomChecker>("test.LocIncDecChecker",77 "MockDescription");78 });79}80 81TEST(RegisterCustomCheckers, CheckLocationIncDec) {82 EXPECT_TRUE(83 runCheckerOnCode<addLocIncDecChecker>("void f() { int *p; (*p)++; }"));84}85 86//===----------------------------------------------------------------------===//87// Unsatisfied checker dependency88//===----------------------------------------------------------------------===//89 90class CheckerRegistrationOrderPrinter91 : 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(OS);104 // Strip a newline off.105 auto R =106 std::make_unique<PathSensitiveBugReport>(BT, OS.str().drop_back(1), N);107 C.emitReport(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(registerCheckerRegistrationOrderPrinter,121 shouldRegisterCheckerRegistrationOrderPrinter,122 "test.RegistrationOrder", "Description");123}124 125#define UNITTEST_CHECKER(CHECKER_NAME) \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"); \141 }142 143UNITTEST_CHECKER(StrongDep)144UNITTEST_CHECKER(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([](CheckerRegistry &Registry) {156 Registry.addChecker(registerStrongDep, shouldRegisterStrongFALSE,157 "test.Strong", "Description");158 addStrongDep(Registry);159 addDep(Registry);160 addCheckerRegistrationOrderPrinter(Registry);161 Registry.addDependency("test.Dep", "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)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([=](CheckerRegistry &Registry) {183 addWeakDep(Registry);184 addDep(Registry);185 addCheckerRegistrationOrderPrinter(Registry);186 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {196 addWeakDep(Registry);197 addDep(Registry);198 addCheckerRegistrationOrderPrinter(Registry);199 Registry.addWeakDependency("test.WeakDep", "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([=](CheckerRegistry &Registry) {209 addWeakDep(Registry);210 addDep(Registry);211 addCheckerRegistrationOrderPrinter(Registry);212 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {221 addWeakDep(Registry);222 addDep(Registry);223 addCheckerRegistrationOrderPrinter(Registry);224 Registry.addWeakDependency("test.Dep", "test.WeakDep");225 });226}227 228UNITTEST_CHECKER(WeakDep2)229UNITTEST_CHECKER(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([=](CheckerRegistry &Registry) {238 addStrongDep(Registry);239 addWeakDep(Registry);240 addWeakDep2(Registry);241 addDep(Registry);242 addDep2(Registry);243 addCheckerRegistrationOrderPrinter(Registry);244 Registry.addWeakDependency("test.Dep", "test.WeakDep");245 Registry.addWeakDependency("test.WeakDep", "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([=](CheckerRegistry &Registry) {256 addStrongDep(Registry);257 addWeakDep(Registry);258 addWeakDep2(Registry);259 addDep(Registry);260 addDep2(Registry);261 addCheckerRegistrationOrderPrinter(Registry);262 Registry.addWeakDependency("test.Dep", "test.WeakDep");263 Registry.addWeakDependency("test.WeakDep", "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([=](CheckerRegistry &Registry) {321 addStrongDep(Registry);322 addWeakDep(Registry);323 addDep(Registry);324 addCheckerRegistrationOrderPrinter(Registry);325 Registry.addDependency("test.WeakDep", "test.StrongDep");326 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {337 addStrongDep(Registry);338 addWeakDep(Registry);339 addDep(Registry);340 addCheckerRegistrationOrderPrinter(Registry);341 Registry.addDependency("test.Dep", "test.StrongDep");342 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {353 addStrongDep(Registry);354 addWeakDep(Registry);355 addDep(Registry);356 addCheckerRegistrationOrderPrinter(Registry);357 Registry.addDependency("test.WeakDep", "test.StrongDep");358 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {368 addStrongDep(Registry);369 addWeakDep(Registry);370 addDep(Registry);371 addCheckerRegistrationOrderPrinter(Registry);372 Registry.addDependency("test.WeakDep", "test.StrongDep");373 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {384 addStrongDep(Registry);385 addWeakDep(Registry);386 addDep(Registry);387 addCheckerRegistrationOrderPrinter(Registry);388 Registry.addDependency("test.WeakDep", "test.StrongDep");389 Registry.addWeakDependency("test.Dep", "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([=](CheckerRegistry &Registry) {400 addStrongDep(Registry);401 addWeakDep(Registry);402 addDep(Registry);403 addDep2(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");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 most420 // important for purely diagnostic checkers that are implemented as a part of421 // purely modeling checkers, because the checker callback order will have to422 // be 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 them445 // 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} // namespace460} // namespace ento461} // namespace clang462