1 //===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/PathDiagnostic.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/Frontend/AnalysisConsumer.h"
16 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "gtest/gtest.h"
23 class OnlyWarningsDiagConsumer
: public PathDiagnosticConsumer
{
24 llvm::raw_ostream
&Output
;
27 OnlyWarningsDiagConsumer(llvm::raw_ostream
&Output
) : Output(Output
) {}
28 void FlushDiagnosticsImpl(std::vector
<const PathDiagnostic
*> &Diags
,
29 FilesMade
*filesMade
) override
{
30 for (const auto *PD
: Diags
) {
31 Output
<< PD
->getCheckerName() << ": ";
32 Output
<< PD
->getShortDescription() << '\n';
36 StringRef
getName() const override
{ return "Test"; }
39 class PathDiagConsumer
: public PathDiagnosticConsumer
{
40 llvm::raw_ostream
&Output
;
43 PathDiagConsumer(llvm::raw_ostream
&Output
) : Output(Output
) {}
44 void FlushDiagnosticsImpl(std::vector
<const PathDiagnostic
*> &Diags
,
45 FilesMade
*filesMade
) override
{
46 for (const auto *PD
: Diags
) {
47 Output
<< PD
->getCheckerName() << ": ";
49 for (PathDiagnosticPieceRef Piece
:
50 PD
->path
.flatten(/*ShouldFlattenMacros*/ true)) {
51 if (Piece
->getKind() != PathDiagnosticPiece::Event
)
53 if (Piece
->getString().empty())
55 // The last event is usually the same as the warning message, skip.
56 if (Piece
->getString() == PD
->getShortDescription())
59 Output
<< Piece
->getString() << " | ";
61 Output
<< PD
->getShortDescription() << '\n';
65 StringRef
getName() const override
{ return "Test"; }
68 using AddCheckerFn
= void(AnalysisASTConsumer
&AnalysisConsumer
,
69 AnalyzerOptions
&AnOpts
);
71 template <AddCheckerFn Fn1
, AddCheckerFn Fn2
, AddCheckerFn
... Fns
>
72 void addChecker(AnalysisASTConsumer
&AnalysisConsumer
,
73 AnalyzerOptions
&AnOpts
) {
74 Fn1(AnalysisConsumer
, AnOpts
);
75 addChecker
<Fn2
, Fns
...>(AnalysisConsumer
, AnOpts
);
78 template <AddCheckerFn Fn1
>
79 void addChecker(AnalysisASTConsumer
&AnalysisConsumer
,
80 AnalyzerOptions
&AnOpts
) {
81 Fn1(AnalysisConsumer
, AnOpts
);
84 template <AddCheckerFn
... Fns
> class TestAction
: public ASTFrontendAction
{
85 llvm::raw_ostream
&DiagsOutput
;
86 bool OnlyEmitWarnings
;
89 TestAction(llvm::raw_ostream
&DiagsOutput
, bool OnlyEmitWarnings
)
90 : DiagsOutput(DiagsOutput
), OnlyEmitWarnings(OnlyEmitWarnings
) {}
92 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
93 StringRef File
) override
{
94 std::unique_ptr
<AnalysisASTConsumer
> AnalysisConsumer
=
95 CreateAnalysisConsumer(Compiler
);
97 AnalysisConsumer
->AddDiagnosticConsumer(
98 new OnlyWarningsDiagConsumer(DiagsOutput
));
100 AnalysisConsumer
->AddDiagnosticConsumer(
101 new PathDiagConsumer(DiagsOutput
));
102 addChecker
<Fns
...>(*AnalysisConsumer
, Compiler
.getAnalyzerOpts());
103 return std::move(AnalysisConsumer
);
107 inline SmallString
<80> getCurrentTestNameAsFileName() {
108 const ::testing::TestInfo
*Info
=
109 ::testing::UnitTest::GetInstance()->current_test_info();
111 SmallString
<80> FileName
;
112 (Twine
{Info
->name()} + ".cc").toVector(FileName
);
116 template <AddCheckerFn
... Fns
>
117 bool runCheckerOnCode(const std::string
&Code
, std::string
&Diags
,
118 bool OnlyEmitWarnings
= false) {
119 const SmallVectorImpl
<char> &FileName
= getCurrentTestNameAsFileName();
120 llvm::raw_string_ostream
OS(Diags
);
121 return tooling::runToolOnCode(
122 std::make_unique
<TestAction
<Fns
...>>(OS
, OnlyEmitWarnings
), Code
,
126 template <AddCheckerFn
... Fns
> bool runCheckerOnCode(const std::string
&Code
) {
128 return runCheckerOnCode
<Fns
...>(Code
, Diags
);
131 template <AddCheckerFn
... Fns
>
132 bool runCheckerOnCodeWithArgs(const std::string
&Code
,
133 const std::vector
<std::string
> &Args
,
135 bool OnlyEmitWarnings
= false) {
136 const SmallVectorImpl
<char> &FileName
= getCurrentTestNameAsFileName();
137 llvm::raw_string_ostream
OS(Diags
);
138 return tooling::runToolOnCodeWithArgs(
139 std::make_unique
<TestAction
<Fns
...>>(OS
, OnlyEmitWarnings
), Code
, Args
,
143 template <AddCheckerFn
... Fns
>
144 bool runCheckerOnCodeWithArgs(const std::string
&Code
,
145 const std::vector
<std::string
> &Args
) {
147 return runCheckerOnCodeWithArgs
<Fns
...>(Code
, Args
, Diags
);