| 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/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 | |
| 24 | namespace clang { |
| 25 | namespace ento { |
| 26 | namespace { |
| 27 | |
| 28 | //===----------------------------------------------------------------------===// |
| 29 | // Just a minimal test for how checker registration works with statically |
| 30 | // linked, non TableGen generated checkers. |
| 31 | //===----------------------------------------------------------------------===// |
| 32 | |
| 33 | class CustomChecker : public Checker<check::ASTCodeBody> { |
| 34 | public: |
| 35 | void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, |
| 36 | BugReporter &BR) const { |
| 37 | BR.EmitBasicReport(DeclWithIssue: D, Checker: this, BugName: "Custom diagnostic" , BugCategory: categories::LogicError, |
| 38 | BugStr: "Custom diagnostic description" , |
| 39 | Loc: PathDiagnosticLocation(D, Mgr.getSourceManager()), Ranges: {}); |
| 40 | } |
| 41 | }; |
| 42 | |
| 43 | void addCustomChecker(AnalysisASTConsumer &AnalysisConsumer, |
| 44 | AnalyzerOptions &AnOpts) { |
| 45 | AnOpts.CheckersAndPackages = {{"test.CustomChecker" , true}}; |
| 46 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) { |
| 47 | Registry.addChecker<CustomChecker>(FullName: "test.CustomChecker" , Desc: "Description" , DocsUri: "" ); |
| 48 | }); |
| 49 | } |
| 50 | |
| 51 | TEST(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 | |
| 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>(Val: 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(Fn: [](CheckerRegistry &Registry) { |
| 76 | Registry.addChecker<CustomChecker>(FullName: "test.LocIncDecChecker" , Desc: "Description" , |
| 77 | DocsUri: "" ); |
| 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 | const BugType BT{this, "Registration order" }; |
| 93 | |
| 94 | public: |
| 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(Out&: OS); |
| 104 | // Strip a newline off. |
| 105 | auto R = |
| 106 | std::make_unique<PathSensitiveBugReport>(args: BT, args: OS.str().drop_back(N: 1), args&: N); |
| 107 | C.emitReport(R: std::move(R)); |
| 108 | } |
| 109 | }; |
| 110 | |
| 111 | void registerCheckerRegistrationOrderPrinter(CheckerManager &mgr) { |
| 112 | mgr.registerChecker<CheckerRegistrationOrderPrinter>(); |
| 113 | } |
| 114 | |
| 115 | bool shouldRegisterCheckerRegistrationOrderPrinter(const CheckerManager &mgr) { |
| 116 | return true; |
| 117 | } |
| 118 | |
| 119 | void addCheckerRegistrationOrderPrinter(CheckerRegistry &Registry) { |
| 120 | Registry.addChecker(Fn: registerCheckerRegistrationOrderPrinter, |
| 121 | sfn: shouldRegisterCheckerRegistrationOrderPrinter, |
| 122 | FullName: "test.RegistrationOrder" , Desc: "Description" , DocsUri: "" , IsHidden: false); |
| 123 | } |
| 124 | |
| 125 | #define UNITTEST_CHECKER(CHECKER_NAME, DIAG_MSG) \ |
| 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", "", false); \ |
| 141 | } |
| 142 | |
| 143 | UNITTEST_CHECKER(StrongDep, "Strong" ) |
| 144 | UNITTEST_CHECKER(Dep, "Dep" ) |
| 145 | |
| 146 | bool shouldRegisterStrongFALSE(const CheckerManager &mgr) { |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | |
| 151 | void addDep(AnalysisASTConsumer &AnalysisConsumer, |
| 152 | AnalyzerOptions &AnOpts) { |
| 153 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 154 | {"test.RegistrationOrder" , true}}; |
| 155 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) { |
| 156 | Registry.addChecker(Fn: registerStrongDep, sfn: shouldRegisterStrongFALSE, |
| 157 | FullName: "test.Strong" , Desc: "Description" , DocsUri: "" , IsHidden: false); |
| 158 | addStrongDep(Registry); |
| 159 | addDep(Registry); |
| 160 | addCheckerRegistrationOrderPrinter(Registry); |
| 161 | Registry.addDependency(FullName: "test.Dep" , Dependency: "test.Strong" ); |
| 162 | }); |
| 163 | } |
| 164 | |
| 165 | TEST(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 | |
| 175 | UNITTEST_CHECKER(WeakDep, "Weak" ) |
| 176 | |
| 177 | void addWeakDepCheckerBothEnabled(AnalysisASTConsumer &AnalysisConsumer, |
| 178 | AnalyzerOptions &AnOpts) { |
| 179 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 180 | {"test.WeakDep" , true}, |
| 181 | {"test.RegistrationOrder" , true}}; |
| 182 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) { |
| 183 | addWeakDep(Registry); |
| 184 | addDep(Registry); |
| 185 | addCheckerRegistrationOrderPrinter(Registry); |
| 186 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 187 | }); |
| 188 | } |
| 189 | |
| 190 | void addWeakDepCheckerBothEnabledSwitched(AnalysisASTConsumer &AnalysisConsumer, |
| 191 | AnalyzerOptions &AnOpts) { |
| 192 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 193 | {"test.WeakDep" , true}, |
| 194 | {"test.RegistrationOrder" , true}}; |
| 195 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) { |
| 196 | addWeakDep(Registry); |
| 197 | addDep(Registry); |
| 198 | addCheckerRegistrationOrderPrinter(Registry); |
| 199 | Registry.addWeakDependency(FullName: "test.WeakDep" , Dependency: "test.Dep" ); |
| 200 | }); |
| 201 | } |
| 202 | |
| 203 | void addWeakDepCheckerDepDisabled(AnalysisASTConsumer &AnalysisConsumer, |
| 204 | AnalyzerOptions &AnOpts) { |
| 205 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 206 | {"test.WeakDep" , false}, |
| 207 | {"test.RegistrationOrder" , true}}; |
| 208 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) { |
| 209 | addWeakDep(Registry); |
| 210 | addDep(Registry); |
| 211 | addCheckerRegistrationOrderPrinter(Registry); |
| 212 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 213 | }); |
| 214 | } |
| 215 | |
| 216 | void addWeakDepCheckerDepUnspecified(AnalysisASTConsumer &AnalysisConsumer, |
| 217 | AnalyzerOptions &AnOpts) { |
| 218 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 219 | {"test.RegistrationOrder" , true}}; |
| 220 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) { |
| 221 | addWeakDep(Registry); |
| 222 | addDep(Registry); |
| 223 | addCheckerRegistrationOrderPrinter(Registry); |
| 224 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 225 | }); |
| 226 | } |
| 227 | |
| 228 | UNITTEST_CHECKER(WeakDep2, "Weak2" ) |
| 229 | UNITTEST_CHECKER(Dep2, "Dep2" ) |
| 230 | |
| 231 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 238 | addStrongDep(Registry); |
| 239 | addWeakDep(Registry); |
| 240 | addWeakDep2(Registry); |
| 241 | addDep(Registry); |
| 242 | addDep2(Registry); |
| 243 | addCheckerRegistrationOrderPrinter(Registry); |
| 244 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 245 | Registry.addWeakDependency(FullName: "test.WeakDep" , Dependency: "test.WeakDep2" ); |
| 246 | }); |
| 247 | } |
| 248 | |
| 249 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 256 | addStrongDep(Registry); |
| 257 | addWeakDep(Registry); |
| 258 | addWeakDep2(Registry); |
| 259 | addDep(Registry); |
| 260 | addDep2(Registry); |
| 261 | addCheckerRegistrationOrderPrinter(Registry); |
| 262 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 263 | Registry.addWeakDependency(FullName: "test.WeakDep" , Dependency: "test.WeakDep2" ); |
| 264 | }); |
| 265 | } |
| 266 | |
| 267 | TEST(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 | |
| 314 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 321 | addStrongDep(Registry); |
| 322 | addWeakDep(Registry); |
| 323 | addDep(Registry); |
| 324 | addCheckerRegistrationOrderPrinter(Registry); |
| 325 | Registry.addDependency(FullName: "test.WeakDep" , Dependency: "test.StrongDep" ); |
| 326 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 327 | }); |
| 328 | } |
| 329 | |
| 330 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 337 | addStrongDep(Registry); |
| 338 | addWeakDep(Registry); |
| 339 | addDep(Registry); |
| 340 | addCheckerRegistrationOrderPrinter(Registry); |
| 341 | Registry.addDependency(FullName: "test.Dep" , Dependency: "test.StrongDep" ); |
| 342 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 343 | }); |
| 344 | } |
| 345 | |
| 346 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 353 | addStrongDep(Registry); |
| 354 | addWeakDep(Registry); |
| 355 | addDep(Registry); |
| 356 | addCheckerRegistrationOrderPrinter(Registry); |
| 357 | Registry.addDependency(FullName: "test.WeakDep" , Dependency: "test.StrongDep" ); |
| 358 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 359 | }); |
| 360 | } |
| 361 | |
| 362 | void addDisabledWeakDepHasUnspecifiedStrongDep( |
| 363 | AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { |
| 364 | AnOpts.CheckersAndPackages = {{"test.Dep" , true}, |
| 365 | {"test.WeakDep" , false}, |
| 366 | {"test.RegistrationOrder" , true}}; |
| 367 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [=](CheckerRegistry &Registry) { |
| 368 | addStrongDep(Registry); |
| 369 | addWeakDep(Registry); |
| 370 | addDep(Registry); |
| 371 | addCheckerRegistrationOrderPrinter(Registry); |
| 372 | Registry.addDependency(FullName: "test.WeakDep" , Dependency: "test.StrongDep" ); |
| 373 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 374 | }); |
| 375 | } |
| 376 | |
| 377 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 384 | addStrongDep(Registry); |
| 385 | addWeakDep(Registry); |
| 386 | addDep(Registry); |
| 387 | addCheckerRegistrationOrderPrinter(Registry); |
| 388 | Registry.addDependency(FullName: "test.WeakDep" , Dependency: "test.StrongDep" ); |
| 389 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 390 | }); |
| 391 | } |
| 392 | |
| 393 | void 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(Fn: [=](CheckerRegistry &Registry) { |
| 400 | addStrongDep(Registry); |
| 401 | addWeakDep(Registry); |
| 402 | addDep(Registry); |
| 403 | addDep2(Registry); |
| 404 | addCheckerRegistrationOrderPrinter(Registry); |
| 405 | Registry.addDependency(FullName: "test.WeakDep" , Dependency: "test.StrongDep" ); |
| 406 | Registry.addDependency(FullName: "test.Dep2" , Dependency: "test.StrongDep" ); |
| 407 | Registry.addWeakDependency(FullName: "test.Dep" , Dependency: "test.WeakDep" ); |
| 408 | }); |
| 409 | } |
| 410 | |
| 411 | TEST(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 most |
| 420 | // important for purely diagnostic checkers that are implemented as a part of |
| 421 | // purely modeling checkers, because the checker callback order will have to |
| 422 | // 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 them |
| 445 | // 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 | } // namespace |
| 460 | } // namespace ento |
| 461 | } // namespace clang |
| 462 | |