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, becuse the checker callback order will have to be |
422 | // 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 | |