xref: /llvm-project/clang/unittests/StaticAnalyzer/CheckerRegistration.h (revision 8e0c9bb91f484b7d2fa86c01919d96a41a7071d7)
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:
OnlyWarningsDiagConsumer(llvm::raw_ostream & Output)27   OnlyWarningsDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)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 
getName()36   StringRef getName() const override { return "Test"; }
37 };
38 
39 class PathDiagConsumer : public PathDiagnosticConsumer {
40   llvm::raw_ostream &Output;
41 
42 public:
PathDiagConsumer(llvm::raw_ostream & Output)43   PathDiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)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*/ 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 
getName()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>
addChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)72 void addChecker(AnalysisASTConsumer &AnalysisConsumer,
73                 AnalyzerOptions &AnOpts) {
74   Fn1(AnalysisConsumer, AnOpts);
75   addChecker<Fn2, Fns...>(AnalysisConsumer, AnOpts);
76 }
77 
78 template <AddCheckerFn Fn1>
addChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)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:
TestAction(llvm::raw_ostream & DiagsOutput,bool OnlyEmitWarnings)89   TestAction(llvm::raw_ostream &DiagsOutput, bool OnlyEmitWarnings)
90       : DiagsOutput(DiagsOutput), OnlyEmitWarnings(OnlyEmitWarnings) {}
91 
CreateASTConsumer(CompilerInstance & Compiler,StringRef File)92   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
93                                                  StringRef File) override {
94     std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =
95         CreateAnalysisConsumer(Compiler);
96     if (OnlyEmitWarnings)
97       AnalysisConsumer->AddDiagnosticConsumer(
98           new OnlyWarningsDiagConsumer(DiagsOutput));
99     else
100       AnalysisConsumer->AddDiagnosticConsumer(
101           new PathDiagConsumer(DiagsOutput));
102     addChecker<Fns...>(*AnalysisConsumer, Compiler.getAnalyzerOpts());
103     return std::move(AnalysisConsumer);
104   }
105 };
106 
getCurrentTestNameAsFileName()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(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       std::make_unique<TestAction<Fns...>>(OS, OnlyEmitWarnings), Code,
123       FileName);
124 }
125 
runCheckerOnCode(const std::string & Code)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>
runCheckerOnCodeWithArgs(const std::string & Code,const std::vector<std::string> & Args)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