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/Frontend/CompilerInstance.h"
10 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
11 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
12 #include "clang/StaticAnalyzer/Core/Checker.h"
13 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
14 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
15 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace ento {
21 
22 class DiagConsumer : public PathDiagnosticConsumer {
23   llvm::raw_ostream &Output;
24 
25 public:
DiagConsumer(llvm::raw_ostream & Output)26   DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
FlushDiagnosticsImpl(std::vector<const PathDiagnostic * > & Diags,FilesMade * filesMade)27   void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
28                             FilesMade *filesMade) override {
29     for (const auto *PD : Diags)
30       Output << PD->getCheckerName() << ":" << PD->getShortDescription() << '\n';
31   }
32 
getName()33   StringRef getName() const override { return "Test"; }
34 };
35 
36 using AddCheckerFn = void(AnalysisASTConsumer &AnalysisConsumer,
37                           AnalyzerOptions &AnOpts);
38 
39 template <AddCheckerFn Fn1, AddCheckerFn Fn2, AddCheckerFn... Fns>
addChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)40 void addChecker(AnalysisASTConsumer &AnalysisConsumer,
41                 AnalyzerOptions &AnOpts) {
42   Fn1(AnalysisConsumer, AnOpts);
43   addChecker<Fn2, Fns...>(AnalysisConsumer, AnOpts);
44 }
45 
46 template <AddCheckerFn Fn1>
addChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)47 void addChecker(AnalysisASTConsumer &AnalysisConsumer,
48                 AnalyzerOptions &AnOpts) {
49   Fn1(AnalysisConsumer, AnOpts);
50 }
51 
52 template <AddCheckerFn... Fns>
53 class TestAction : public ASTFrontendAction {
54   llvm::raw_ostream &DiagsOutput;
55 
56 public:
TestAction(llvm::raw_ostream & DiagsOutput)57   TestAction(llvm::raw_ostream &DiagsOutput) : DiagsOutput(DiagsOutput) {}
58 
CreateASTConsumer(CompilerInstance & Compiler,StringRef File)59   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
60                                                  StringRef File) override {
61     std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer =
62         CreateAnalysisConsumer(Compiler);
63     AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput));
64     addChecker<Fns...>(*AnalysisConsumer, *Compiler.getAnalyzerOpts());
65     return std::move(AnalysisConsumer);
66   }
67 };
68 
getCurrentTestNameAsFileName()69 inline SmallString<80> getCurrentTestNameAsFileName() {
70   const ::testing::TestInfo *Info =
71       ::testing::UnitTest::GetInstance()->current_test_info();
72 
73   SmallString<80> FileName;
74   (Twine{Info->name()} + ".cc").toVector(FileName);
75   return FileName;
76 }
77 
78 template <AddCheckerFn... Fns>
runCheckerOnCode(const std::string & Code,std::string & Diags)79 bool runCheckerOnCode(const std::string &Code, std::string &Diags) {
80   const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
81   llvm::raw_string_ostream OS(Diags);
82   return tooling::runToolOnCode(std::make_unique<TestAction<Fns...>>(OS), Code,
83                                 FileName);
84 }
85 
86 template <AddCheckerFn... Fns>
runCheckerOnCode(const std::string & Code)87 bool runCheckerOnCode(const std::string &Code) {
88   std::string Diags;
89   return runCheckerOnCode<Fns...>(Code, Diags);
90 }
91 
92 template <AddCheckerFn... Fns>
runCheckerOnCodeWithArgs(const std::string & Code,const std::vector<std::string> & Args,std::string & Diags)93 bool runCheckerOnCodeWithArgs(const std::string &Code,
94                               const std::vector<std::string> &Args,
95                               std::string &Diags) {
96   const SmallVectorImpl<char> &FileName = getCurrentTestNameAsFileName();
97   llvm::raw_string_ostream OS(Diags);
98   return tooling::runToolOnCodeWithArgs(
99       std::make_unique<TestAction<Fns...>>(OS), Code, Args, FileName);
100 }
101 
102 template <AddCheckerFn... Fns>
runCheckerOnCodeWithArgs(const std::string & Code,const std::vector<std::string> & Args)103 bool runCheckerOnCodeWithArgs(const std::string &Code,
104                               const std::vector<std::string> &Args) {
105   std::string Diags;
106   return runCheckerOnCodeWithArgs<Fns...>(Code, Args, Diags);
107 }
108 
109 } // namespace ento
110 } // namespace clang
111