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