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 "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" |
19 | |
20 | namespace clang { |
21 | namespace ento { |
22 | |
23 | class OnlyWarningsDiagConsumer : public PathDiagnosticConsumer { |
24 | llvm::raw_ostream &Output; |
25 | |
26 | public: |
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'; |
33 | } |
34 | } |
35 | |
36 | StringRef getName() const override { return "Test" ; } |
37 | }; |
38 | |
39 | class PathDiagConsumer : public PathDiagnosticConsumer { |
40 | llvm::raw_ostream &Output; |
41 | |
42 | public: |
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() << ": " ; |
48 | |
49 | for (PathDiagnosticPieceRef Piece : |
50 | PD->path.flatten(/*ShouldFlattenMacros*/ ShouldFlattenMacros: true)) { |
51 | if (Piece->getKind() != PathDiagnosticPiece::Event) |
52 | continue; |
53 | if (Piece->getString().empty()) |
54 | continue; |
55 | // The last event is usually the same as the warning message, skip. |
56 | if (Piece->getString() == PD->getShortDescription()) |
57 | continue; |
58 | |
59 | Output << Piece->getString() << " | " ; |
60 | } |
61 | Output << PD->getShortDescription() << '\n'; |
62 | } |
63 | } |
64 | |
65 | StringRef getName() const override { return "Test" ; } |
66 | }; |
67 | |
68 | using AddCheckerFn = void(AnalysisASTConsumer &AnalysisConsumer, |
69 | AnalyzerOptions &AnOpts); |
70 | |
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); |
76 | } |
77 | |
78 | template <AddCheckerFn Fn1> |
79 | void addChecker(AnalysisASTConsumer &AnalysisConsumer, |
80 | AnalyzerOptions &AnOpts) { |
81 | Fn1(AnalysisConsumer, AnOpts); |
82 | } |
83 | |
84 | template <AddCheckerFn... Fns> class TestAction : public ASTFrontendAction { |
85 | llvm::raw_ostream &DiagsOutput; |
86 | bool OnlyEmitWarnings; |
87 | |
88 | public: |
89 | TestAction(llvm::raw_ostream &DiagsOutput, bool OnlyEmitWarnings) |
90 | : DiagsOutput(DiagsOutput), OnlyEmitWarnings(OnlyEmitWarnings) {} |
91 | |
92 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
93 | StringRef File) override { |
94 | std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer = |
95 | CreateAnalysisConsumer(CI&: Compiler); |
96 | if (OnlyEmitWarnings) |
97 | AnalysisConsumer->AddDiagnosticConsumer( |
98 | Consumer: new OnlyWarningsDiagConsumer(DiagsOutput)); |
99 | else |
100 | AnalysisConsumer->AddDiagnosticConsumer( |
101 | Consumer: new PathDiagConsumer(DiagsOutput)); |
102 | addChecker<Fns...>(*AnalysisConsumer, Compiler.getAnalyzerOpts()); |
103 | return std::move(AnalysisConsumer); |
104 | } |
105 | }; |
106 | |
107 | inline SmallString<80> getCurrentTestNameAsFileName() { |
108 | const ::testing::TestInfo *Info = |
109 | ::testing::UnitTest::GetInstance()->current_test_info(); |
110 | |
111 | SmallString<80> FileName; |
112 | (Twine{Info->name()} + ".cc" ).toVector(Out&: FileName); |
113 | return FileName; |
114 | } |
115 | |
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 | ToolAction: std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code, |
123 | FileName); |
124 | } |
125 | |
126 | template <AddCheckerFn... Fns> bool runCheckerOnCode(const std::string &Code) { |
127 | std::string Diags; |
128 | return runCheckerOnCode<Fns...>(Code, Diags); |
129 | } |
130 | |
131 | template <AddCheckerFn... Fns> |
132 | bool runCheckerOnCodeWithArgs(const std::string &Code, |
133 | const std::vector<std::string> &Args, |
134 | std::string &Diags, |
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, |
140 | FileName); |
141 | } |
142 | |
143 | template <AddCheckerFn... Fns> |
144 | bool runCheckerOnCodeWithArgs(const std::string &Code, |
145 | const std::vector<std::string> &Args) { |
146 | std::string Diags; |
147 | return runCheckerOnCodeWithArgs<Fns...>(Code, Args, Diags); |
148 | } |
149 | |
150 | } // namespace ento |
151 | } // namespace clang |
152 | |