[libc] Switch to using the generic `<gpuintrin.h>` implementations (#121810)
[llvm-project.git] / clang / unittests / StaticAnalyzer / CallDescriptionTest.cpp
blobaed49a1f5f78de18d7b5861eaa3d2cc2664d8edd
1 //===- unittests/StaticAnalyzer/CallDescriptionTest.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 //===----------------------------------------------------------------------===//
9 #include "CheckerRegistration.h"
10 #include "Reusables.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/Analysis/PathDiagnostic.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
20 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
21 #include "clang/Tooling/Tooling.h"
22 #include "gtest/gtest.h"
23 #include <type_traits>
25 namespace clang {
26 namespace ento {
27 namespace {
29 // A wrapper around CallDescriptionMap<bool> that allows verifying that
30 // all functions have been found. This is needed because CallDescriptionMap
31 // isn't supposed to support iteration.
32 class ResultMap {
33 size_t Found, Total;
34 CallDescriptionMap<bool> Impl;
36 public:
37 ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data)
38 : Found(0),
39 Total(std::count_if(Data.begin(), Data.end(),
40 [](const std::pair<CallDescription, bool> &Pair) {
41 return Pair.second == true;
42 })),
43 Impl(std::move(Data)) {}
45 const bool *lookup(const CallEvent &Call) {
46 const bool *Result = Impl.lookup(Call);
47 // If it's a function we expected to find, remember that we've found it.
48 if (Result && *Result)
49 ++Found;
50 return Result;
53 // Fail the test if we haven't found all the true-calls we were looking for.
54 ~ResultMap() { EXPECT_EQ(Found, Total); }
57 // Scan the code body for call expressions and see if we find all calls that
58 // we were supposed to find ("true" in the provided ResultMap) and that we
59 // don't find the ones that we weren't supposed to find
60 // ("false" in the ResultMap).
61 template <typename MatchedExprT>
62 class CallDescriptionConsumer : public ExprEngineConsumer {
63 ResultMap &RM;
64 void performTest(const Decl *D) {
65 using namespace ast_matchers;
66 using T = MatchedExprT;
68 if (!D->hasBody())
69 return;
71 const StackFrameContext *SFC =
72 Eng.getAnalysisDeclContextManager().getStackFrame(D);
73 const ProgramStateRef State = Eng.getInitialState(SFC);
75 // FIXME: Maybe use std::variant and std::visit for these.
76 const auto MatcherCreator = []() {
77 if (std::is_same<T, CallExpr>::value)
78 return callExpr();
79 if (std::is_same<T, CXXConstructExpr>::value)
80 return cxxConstructExpr();
81 if (std::is_same<T, CXXMemberCallExpr>::value)
82 return cxxMemberCallExpr();
83 if (std::is_same<T, CXXOperatorCallExpr>::value)
84 return cxxOperatorCallExpr();
85 llvm_unreachable("Only these expressions are supported for now.");
88 const Expr *E = findNode<T>(D, MatcherCreator());
90 CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager();
91 CallEventRef<> Call = [=, &CEMgr]() -> CallEventRef<CallEvent> {
92 CFGBlock::ConstCFGElementRef ElemRef = {SFC->getCallSiteBlock(),
93 SFC->getIndex()};
94 if (std::is_base_of<CallExpr, T>::value)
95 return CEMgr.getCall(E, State, SFC, ElemRef);
96 if (std::is_same<T, CXXConstructExpr>::value)
97 return CEMgr.getCXXConstructorCall(cast<CXXConstructExpr>(E),
98 /*Target=*/nullptr, State, SFC,
99 ElemRef);
100 llvm_unreachable("Only these expressions are supported for now.");
101 }();
103 // If the call actually matched, check if we really expected it to match.
104 const bool *LookupResult = RM.lookup(*Call);
105 EXPECT_TRUE(!LookupResult || *LookupResult);
107 // ResultMap is responsible for making sure that we've found *all* calls.
110 public:
111 CallDescriptionConsumer(CompilerInstance &C,
112 ResultMap &RM)
113 : ExprEngineConsumer(C), RM(RM) {}
115 bool HandleTopLevelDecl(DeclGroupRef DG) override {
116 for (const auto *D : DG)
117 performTest(D);
118 return true;
122 template <typename MatchedExprT = CallExpr>
123 class CallDescriptionAction : public ASTFrontendAction {
124 ResultMap RM;
126 public:
127 CallDescriptionAction(
128 std::initializer_list<std::pair<CallDescription, bool>> Data)
129 : RM(Data) {}
131 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
132 StringRef File) override {
133 return std::make_unique<CallDescriptionConsumer<MatchedExprT>>(Compiler,
134 RM);
138 TEST(CallDescription, SimpleNameMatching) {
139 EXPECT_TRUE(tooling::runToolOnCode(
140 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
141 {{CDM::SimpleFunc, {"bar"}},
142 false}, // false: there's no call to 'bar' in this code.
143 {{CDM::SimpleFunc, {"foo"}},
144 true}, // true: there's a call to 'foo' in this code.
145 })),
146 "void foo(); void bar() { foo(); }"));
149 TEST(CallDescription, RequiredArguments) {
150 EXPECT_TRUE(tooling::runToolOnCode(
151 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
152 {{CDM::SimpleFunc, {"foo"}, 1}, true},
153 {{CDM::SimpleFunc, {"foo"}, 2}, false},
154 })),
155 "void foo(int); void foo(int, int); void bar() { foo(1); }"));
158 TEST(CallDescription, LackOfRequiredArguments) {
159 EXPECT_TRUE(tooling::runToolOnCode(
160 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
161 {{CDM::SimpleFunc, {"foo"}, std::nullopt}, true},
162 {{CDM::SimpleFunc, {"foo"}, 2}, false},
163 })),
164 "void foo(int); void foo(int, int); void bar() { foo(1); }"));
167 constexpr StringRef MockStdStringHeader = R"code(
168 namespace std { inline namespace __1 {
169 template<typename T> class basic_string {
170 class Allocator {};
171 public:
172 basic_string();
173 explicit basic_string(const char*, const Allocator & = Allocator());
174 ~basic_string();
175 T *c_str();
177 } // namespace __1
178 using string = __1::basic_string<char>;
179 } // namespace std
180 )code";
182 TEST(CallDescription, QualifiedNames) {
183 constexpr StringRef AdditionalCode = R"code(
184 void foo() {
185 using namespace std;
186 basic_string<char> s;
187 s.c_str();
188 })code";
189 const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str();
190 EXPECT_TRUE(tooling::runToolOnCode(
191 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
192 {{CDM::CXXMethod, {"std", "basic_string", "c_str"}}, true},
193 })),
194 Code));
197 TEST(CallDescription, MatchConstructor) {
198 constexpr StringRef AdditionalCode = R"code(
199 void foo() {
200 using namespace std;
201 basic_string<char> s("hello");
202 })code";
203 const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str();
204 EXPECT_TRUE(tooling::runToolOnCode(
205 std::unique_ptr<FrontendAction>(
206 new CallDescriptionAction<CXXConstructExpr>({
207 {{CDM::CXXMethod, {"std", "basic_string", "basic_string"}, 2, 2},
208 true},
209 })),
210 Code));
213 // FIXME: Test matching destructors: {"std", "basic_string", "~basic_string"}
214 // This feature is actually implemented, but the test infra is not yet
215 // sophisticated enough for testing this. To do that, we will need to
216 // implement a much more advanced dispatching mechanism using the CFG for
217 // the implicit destructor events.
219 TEST(CallDescription, MatchConversionOperator) {
220 constexpr StringRef Code = R"code(
221 namespace aaa {
222 namespace bbb {
223 struct Bar {
224 operator int();
226 } // bbb
227 } // aaa
228 void foo() {
229 aaa::bbb::Bar x;
230 int tmp = x;
231 })code";
232 EXPECT_TRUE(tooling::runToolOnCode(
233 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
234 {{CDM::CXXMethod, {"aaa", "bbb", "Bar", "operator int"}}, true},
235 })),
236 Code));
239 TEST(CallDescription, RejectOverQualifiedNames) {
240 constexpr auto Code = R"code(
241 namespace my {
242 namespace std {
243 struct container {
244 const char *data() const;
246 } // namespace std
247 } // namespace my
249 void foo() {
250 using namespace my;
251 std::container v;
252 v.data();
253 })code";
255 // FIXME: We should **not** match.
256 EXPECT_TRUE(tooling::runToolOnCode(
257 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
258 {{CDM::CXXMethod, {"std", "container", "data"}}, true},
259 })),
260 Code));
263 TEST(CallDescription, DontSkipNonInlineNamespaces) {
264 constexpr auto Code = R"code(
265 namespace my {
266 /*not inline*/ namespace v1 {
267 void bar();
268 } // namespace v1
269 } // namespace my
270 void foo() {
271 my::v1::bar();
272 })code";
275 SCOPED_TRACE("my v1 bar");
276 EXPECT_TRUE(tooling::runToolOnCode(
277 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
278 {{CDM::SimpleFunc, {"my", "v1", "bar"}}, true},
279 })),
280 Code));
283 // FIXME: We should **not** skip non-inline namespaces.
284 SCOPED_TRACE("my bar");
285 EXPECT_TRUE(tooling::runToolOnCode(
286 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
287 {{CDM::SimpleFunc, {"my", "bar"}}, true},
288 })),
289 Code));
293 TEST(CallDescription, SkipTopInlineNamespaces) {
294 constexpr auto Code = R"code(
295 inline namespace my {
296 namespace v1 {
297 void bar();
298 } // namespace v1
299 } // namespace my
300 void foo() {
301 using namespace v1;
302 bar();
303 })code";
306 SCOPED_TRACE("my v1 bar");
307 EXPECT_TRUE(tooling::runToolOnCode(
308 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
309 {{CDM::SimpleFunc, {"my", "v1", "bar"}}, true},
310 })),
311 Code));
314 SCOPED_TRACE("v1 bar");
315 EXPECT_TRUE(tooling::runToolOnCode(
316 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
317 {{CDM::SimpleFunc, {"v1", "bar"}}, true},
318 })),
319 Code));
323 TEST(CallDescription, SkipAnonimousNamespaces) {
324 constexpr auto Code = R"code(
325 namespace {
326 namespace std {
327 namespace {
328 inline namespace {
329 struct container {
330 const char *data() const { return nullptr; };
332 } // namespace inline anonymous
333 } // namespace anonymous
334 } // namespace std
335 } // namespace anonymous
337 void foo() {
338 std::container v;
339 v.data();
340 })code";
342 EXPECT_TRUE(tooling::runToolOnCode(
343 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
344 {{CDM::CXXMethod, {"std", "container", "data"}}, true},
345 })),
346 Code));
349 TEST(CallDescription, AliasNames) {
350 constexpr StringRef AliasNamesCode = R"code(
351 namespace std {
352 struct container {
353 const char *data() const;
355 using cont = container;
356 } // std
357 )code";
359 constexpr StringRef UseAliasInSpelling = R"code(
360 void foo() {
361 std::cont v;
362 v.data();
363 })code";
364 constexpr StringRef UseStructNameInSpelling = R"code(
365 void foo() {
366 std::container v;
367 v.data();
368 })code";
369 const std::string UseAliasInSpellingCode =
370 (Twine{AliasNamesCode} + UseAliasInSpelling).str();
371 const std::string UseStructNameInSpellingCode =
372 (Twine{AliasNamesCode} + UseStructNameInSpelling).str();
374 // Test if the code spells the alias, wile we match against the struct name,
375 // and again matching against the alias.
377 SCOPED_TRACE("Using alias in spelling");
379 SCOPED_TRACE("std container data");
380 EXPECT_TRUE(tooling::runToolOnCode(
381 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
382 {{CDM::CXXMethod, {"std", "container", "data"}}, true},
383 })),
384 UseAliasInSpellingCode));
387 // FIXME: We should be able to see-through aliases.
388 SCOPED_TRACE("std cont data");
389 EXPECT_TRUE(tooling::runToolOnCode(
390 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
391 {{CDM::CXXMethod, {"std", "cont", "data"}}, false},
392 })),
393 UseAliasInSpellingCode));
397 // Test if the code spells the struct name, wile we match against the struct
398 // name, and again matching against the alias.
400 SCOPED_TRACE("Using struct name in spelling");
402 SCOPED_TRACE("std container data");
403 EXPECT_TRUE(tooling::runToolOnCode(
404 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
405 {{CDM::CXXMethod, {"std", "container", "data"}}, true},
406 })),
407 UseAliasInSpellingCode));
410 // FIXME: We should be able to see-through aliases.
411 SCOPED_TRACE("std cont data");
412 EXPECT_TRUE(tooling::runToolOnCode(
413 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
414 {{CDM::CXXMethod, {"std", "cont", "data"}}, false},
415 })),
416 UseAliasInSpellingCode));
421 TEST(CallDescription, AliasSingleNamespace) {
422 constexpr StringRef Code = R"code(
423 namespace aaa {
424 namespace bbb {
425 namespace ccc {
426 void bar();
427 }} // namespace bbb::ccc
428 namespace bbb_alias = bbb;
429 } // namespace aaa
430 void foo() {
431 aaa::bbb_alias::ccc::bar();
432 })code";
434 SCOPED_TRACE("aaa bbb ccc bar");
435 EXPECT_TRUE(tooling::runToolOnCode(
436 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
437 {{CDM::SimpleFunc, {"aaa", "bbb", "ccc", "bar"}}, true},
438 })),
439 Code));
442 // FIXME: We should be able to see-through namespace aliases.
443 SCOPED_TRACE("aaa bbb_alias ccc bar");
444 EXPECT_TRUE(tooling::runToolOnCode(
445 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
446 {{CDM::SimpleFunc, {"aaa", "bbb_alias", "ccc", "bar"}}, false},
447 })),
448 Code));
452 TEST(CallDescription, AliasMultipleNamespaces) {
453 constexpr StringRef Code = R"code(
454 namespace aaa {
455 namespace bbb {
456 namespace ccc {
457 void bar();
458 }}} // namespace aaa::bbb::ccc
459 namespace aaa_bbb_ccc = aaa::bbb::ccc;
460 void foo() {
461 using namespace aaa_bbb_ccc;
462 bar();
463 })code";
465 SCOPED_TRACE("aaa bbb ccc bar");
466 EXPECT_TRUE(tooling::runToolOnCode(
467 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
468 {{CDM::SimpleFunc, {"aaa", "bbb", "ccc", "bar"}}, true},
469 })),
470 Code));
473 // FIXME: We should be able to see-through namespace aliases.
474 SCOPED_TRACE("aaa_bbb_ccc bar");
475 EXPECT_TRUE(tooling::runToolOnCode(
476 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
477 {{CDM::SimpleFunc, {"aaa_bbb_ccc", "bar"}}, false},
478 })),
479 Code));
483 TEST(CallDescription, NegativeMatchQualifiedNames) {
484 EXPECT_TRUE(tooling::runToolOnCode(
485 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
486 {{CDM::Unspecified, {"foo", "bar"}}, false},
487 {{CDM::Unspecified, {"bar", "foo"}}, false},
488 {{CDM::Unspecified, {"foo"}}, true},
489 })),
490 "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
493 TEST(CallDescription, MatchBuiltins) {
494 // Test the matching modes CDM::CLibrary and CDM::CLibraryMaybeHardened,
495 // which can recognize builtin variants of C library functions.
497 SCOPED_TRACE("hardened variants of functions");
498 EXPECT_TRUE(tooling::runToolOnCode(
499 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
500 {{{CDM::Unspecified, {"memset"}, 3}, false},
501 {{CDM::CLibrary, {"memset"}, 3}, false},
502 {{CDM::CLibraryMaybeHardened, {"memset"}, 3}, true}})),
503 "void foo() {"
504 " int x;"
505 " __builtin___memset_chk(&x, 0, sizeof(x),"
506 " __builtin_object_size(&x, 0));"
507 "}"));
510 SCOPED_TRACE("multiple similar builtins");
511 EXPECT_TRUE(tooling::runToolOnCode(
512 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
513 {{{CDM::CLibrary, {"memcpy"}, 3}, false},
514 {{CDM::CLibrary, {"wmemcpy"}, 3}, true}})),
515 R"(void foo(wchar_t *x, wchar_t *y) {
516 __builtin_wmemcpy(x, y, sizeof(wchar_t));
517 })"));
520 SCOPED_TRACE("multiple similar builtins reversed order");
521 EXPECT_TRUE(tooling::runToolOnCode(
522 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
523 {{{CDM::CLibrary, {"wmemcpy"}, 3}, true},
524 {{CDM::CLibrary, {"memcpy"}, 3}, false}})),
525 R"(void foo(wchar_t *x, wchar_t *y) {
526 __builtin_wmemcpy(x, y, sizeof(wchar_t));
527 })"));
530 SCOPED_TRACE("multiple similar builtins with hardened variant");
531 EXPECT_TRUE(tooling::runToolOnCode(
532 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
533 {{{CDM::CLibraryMaybeHardened, {"memcpy"}, 3}, false},
534 {{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3}, true}})),
535 R"(typedef __typeof(sizeof(int)) size_t;
536 extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
537 const wchar_t *__restrict __s2,
538 size_t __n, size_t __ns1);
539 void foo(wchar_t *x, wchar_t *y) {
540 __wmemcpy_chk(x, y, sizeof(wchar_t), 1234);
541 })"));
544 SCOPED_TRACE(
545 "multiple similar builtins with hardened variant reversed order");
546 EXPECT_TRUE(tooling::runToolOnCode(
547 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
548 {{{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3}, true},
549 {{CDM::CLibraryMaybeHardened, {"memcpy"}, 3}, false}})),
550 R"(typedef __typeof(sizeof(int)) size_t;
551 extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
552 const wchar_t *__restrict __s2,
553 size_t __n, size_t __ns1);
554 void foo(wchar_t *x, wchar_t *y) {
555 __wmemcpy_chk(x, y, sizeof(wchar_t), 1234);
556 })"));
559 SCOPED_TRACE("lookbehind and lookahead mismatches");
560 EXPECT_TRUE(tooling::runToolOnCode(
561 std::unique_ptr<FrontendAction>(
562 new CallDescriptionAction<>({{{CDM::CLibrary, {"func"}}, false}})),
564 void funcXXX();
565 void XXXfunc();
566 void XXXfuncXXX();
567 void test() {
568 funcXXX();
569 XXXfunc();
570 XXXfuncXXX();
571 })"));
574 SCOPED_TRACE("lookbehind and lookahead matches");
575 EXPECT_TRUE(tooling::runToolOnCode(
576 std::unique_ptr<FrontendAction>(
577 new CallDescriptionAction<>({{{CDM::CLibrary, {"func"}}, true}})),
579 void func();
580 void func_XXX();
581 void XXX_func();
582 void XXX_func_XXX();
584 void test() {
585 func(); // exact match
586 func_XXX();
587 XXX_func();
588 XXX_func_XXX();
589 })"));
593 //===----------------------------------------------------------------------===//
594 // Testing through a checker interface.
596 // Above, the static analyzer isn't run properly, only the bare minimum to
597 // create CallEvents. This causes CallEvents through function pointers to not
598 // refer to the pointee function, but this works fine if we run
599 // AnalysisASTConsumer.
600 //===----------------------------------------------------------------------===//
602 class CallDescChecker
603 : public Checker<check::PreCall, check::PreStmt<CallExpr>> {
604 CallDescriptionSet Set = {{CDM::SimpleFunc, {"bar"}, 0}};
606 public:
607 void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
608 if (Set.contains(Call)) {
609 C.getBugReporter().EmitBasicReport(
610 Call.getDecl(), this, "CallEvent match", categories::LogicError,
611 "CallEvent match",
612 PathDiagnosticLocation{Call.getDecl(), C.getSourceManager()});
616 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
617 if (Set.containsAsWritten(*CE)) {
618 C.getBugReporter().EmitBasicReport(
619 CE->getCalleeDecl(), this, "CallExpr match", categories::LogicError,
620 "CallExpr match",
621 PathDiagnosticLocation{CE->getCalleeDecl(), C.getSourceManager()});
626 void addCallDescChecker(AnalysisASTConsumer &AnalysisConsumer,
627 AnalyzerOptions &AnOpts) {
628 AnOpts.CheckersAndPackages = {{"test.CallDescChecker", true}};
629 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
630 Registry.addChecker<CallDescChecker>("test.CallDescChecker", "Description",
631 "");
635 TEST(CallDescription, CheckCallExprMatching) {
636 // Imprecise matching shouldn't catch the call to bar, because its obscured
637 // by a function pointer.
638 constexpr StringRef FnPtrCode = R"code(
639 void bar();
640 void foo() {
641 void (*fnptr)() = bar;
642 fnptr();
643 })code";
644 std::string Diags;
645 EXPECT_TRUE(runCheckerOnCode<addCallDescChecker>(FnPtrCode.str(), Diags,
646 /*OnlyEmitWarnings*/ true));
647 EXPECT_EQ("test.CallDescChecker: CallEvent match\n", Diags);
649 // This should be caught properly by imprecise matching, as the call is done
650 // purely through syntactic means.
651 constexpr StringRef Code = R"code(
652 void bar();
653 void foo() {
654 bar();
655 })code";
656 Diags.clear();
657 EXPECT_TRUE(runCheckerOnCode<addCallDescChecker>(Code.str(), Diags,
658 /*OnlyEmitWarnings*/ true));
659 EXPECT_EQ("test.CallDescChecker: CallEvent match\n"
660 "test.CallDescChecker: CallExpr match\n",
661 Diags);
664 } // namespace
665 } // namespace ento
666 } // namespace clang